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/sock_daemon_test.c | 1980 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1980 insertions(+) create mode 100644 ctdb/tests/src/sock_daemon_test.c (limited to 'ctdb/tests/src/sock_daemon_test.c') diff --git a/ctdb/tests/src/sock_daemon_test.c b/ctdb/tests/src/sock_daemon_test.c new file mode 100644 index 0000000..acafc9f --- /dev/null +++ b/ctdb/tests/src/sock_daemon_test.c @@ -0,0 +1,1980 @@ +/* + sock daemon tests + + Copyright (C) Amitay Isaacs 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 . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" +#include "system/wait.h" + +#include + +#include "common/logging.c" +#include "common/pkt_read.c" +#include "common/pkt_write.c" +#include "common/comm.c" +#include "common/pidfile.c" +#include "common/sock_daemon.c" +#include "common/sock_io.c" + +struct dummy_wait_state { +}; + +static struct tevent_req *dummy_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req; + struct dummy_wait_state *state; + const char *sockpath = (const char *)private_data; + struct stat st; + int ret; + + ret = stat(sockpath, &st); + assert(ret == 0); + assert(S_ISSOCK(st.st_mode)); + + req = tevent_req_create(mem_ctx, &state, struct dummy_wait_state); + if (req == NULL) { + return NULL; + } + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static bool dummy_wait_recv(struct tevent_req *req, int *perr) +{ + return true; +} + +static int test1_startup_fail(void *private_data) +{ + return 1; +} + +static int test1_startup(void *private_data) +{ + const char *sockpath = (const char *)private_data; + struct stat st; + int ret; + + ret = stat(sockpath, &st); + assert(ret == -1); + + return 0; +} + +struct test1_startup_state { +}; + +static struct tevent_req *test1_startup_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req; + struct test1_startup_state *state; + + req = tevent_req_create(mem_ctx, &state, struct test1_startup_state); + if (req == NULL) { + return NULL; + } + + tevent_req_error(req, 2); + return tevent_req_post(req, ev); +} + +static bool test1_startup_recv(struct tevent_req *req, int *perr) +{ + if (tevent_req_is_unix_error(req, perr)) { + return false; + } + + return true; +} + +static struct tevent_req *dummy_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data) +{ + return NULL; +} + +static bool dummy_read_recv(struct tevent_req *req, int *perr) +{ + if (perr != NULL) { + *perr = EINVAL; + } + return false; +} + +static struct sock_socket_funcs dummy_socket_funcs = { + .read_send = dummy_read_send, + .read_recv = dummy_read_recv, +}; + +/* + * test1 + * + * Check setup without actually running daemon + */ + +static void test1(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct sock_daemon_funcs test1_funcs; + struct stat st; + int ret; + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + test1_funcs = (struct sock_daemon_funcs){ + .startup = test1_startup_fail, + }; + + ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE", + &test1_funcs, NULL, &sockd); + assert(ret == 0); + assert(sockd != NULL); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = sock_daemon_run(ev, sockd, NULL, false, false, -1); + assert(ret == EIO); + talloc_free(sockd); + + test1_funcs = (struct sock_daemon_funcs){ + .startup_send = test1_startup_send, + .startup_recv = test1_startup_recv, + }; + + ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE", + &test1_funcs, NULL, &sockd); + assert(ret == 0); + assert(sockd != NULL); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = sock_daemon_run(ev, sockd, NULL, false, false, -1); + assert(ret == EIO); + talloc_free(sockd); + + test1_funcs = (struct sock_daemon_funcs){ + .startup = test1_startup, + .wait_send = dummy_wait_send, + .wait_recv = dummy_wait_recv, + }; + + ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE", + &test1_funcs, discard_const(sockpath), &sockd); + assert(ret == 0); + assert(sockd != NULL); + + ret = sock_daemon_add_unix(sockd, sockpath, &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = stat(sockpath, &st); + assert(ret == -1); + + ret = sock_daemon_run(ev, sockd, NULL, false, false, -1); + assert(ret == 0); + + talloc_free(mem_ctx); +} + +/* + * test2 + * + * Start daemon, check PID file, sock daemon functions, termination, + * exit code + */ + +static int test2_startup(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 1; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + return 0; +} + +static int test2_reconfigure(void *private_data) +{ + static bool first_time = true; + int fd = *(int *)private_data; + int ret = 2; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + + if (first_time) { + first_time = false; + return 1; + } + + return 0; +} + +struct test2_reconfigure_state { + int fd; +}; + +static struct tevent_req *test2_reconfigure_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req; + struct test2_reconfigure_state *state; + static bool first_time = true; + + req = tevent_req_create(mem_ctx, &state, + struct test2_reconfigure_state); + if (req == NULL) { + return NULL; + } + + state->fd = *(int *)private_data; + + if (first_time) { + first_time = false; + tevent_req_error(req, 2); + } else { + tevent_req_done(req); + } + + return tevent_req_post(req, ev); +} + +static bool test2_reconfigure_recv(struct tevent_req *req, int *perr) +{ + struct test2_reconfigure_state *state = tevent_req_data( + req, struct test2_reconfigure_state); + int ret = 2; + ssize_t nwritten; + + nwritten = write(state->fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + + if (tevent_req_is_unix_error(req, perr)) { + return false; + } + + return true; +} + +static int test2_reopen_logs(void *private_data) +{ + static bool first_time = true; + int fd = *(int *)private_data; + int ret = 4; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + + if (first_time) { + first_time = false; + return 1; + } + + return 0; +} + +struct test2_reopen_logs_state { + int fd; +}; + +static struct tevent_req *test2_reopen_logs_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req; + struct test2_reopen_logs_state *state; + static bool first_time = true; + + req = tevent_req_create(mem_ctx, &state, + struct test2_reopen_logs_state); + if (req == NULL) { + return NULL; + } + + state->fd = *(int *)private_data; + + if (first_time) { + first_time = false; + tevent_req_error(req, 2); + } else { + tevent_req_done(req); + } + + return tevent_req_post(req, ev); +} + +static bool test2_reopen_logs_recv(struct tevent_req *req, int *perr) +{ + struct test2_reopen_logs_state *state = tevent_req_data( + req, struct test2_reopen_logs_state); + int ret = 4; + ssize_t nwritten; + + nwritten = write(state->fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + + if (tevent_req_is_unix_error(req, perr)) { + return false; + } + + return true; +} + +static void test2_shutdown(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 3; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); +} + +struct test2_shutdown_state { + int fd; +}; + +static struct tevent_req *test2_shutdown_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req; + struct test2_shutdown_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct test2_shutdown_state); + if (req == NULL) { + return NULL; + } + + state->fd = *(int *)private_data; + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static void test2_shutdown_recv(struct tevent_req *req) +{ + struct test2_shutdown_state *state = tevent_req_data( + req, struct test2_shutdown_state); + int ret = 3; + ssize_t nwritten; + + nwritten = write(state->fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); +} + +static void test2(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + int fd[2]; + pid_t pid, pid2; + int ret; + ssize_t n; + int pidfile_fd; + char pidstr[20] = { 0 }; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct sock_daemon_funcs test2_funcs = { + .startup = test2_startup, + .reconfigure = test2_reconfigure, + .reopen_logs = test2_reopen_logs, + .shutdown = test2_shutdown, + }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test2", "file:", "NOTICE", + &test2_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + pidfile_fd = open(pidfile, O_RDONLY, 0644); + assert(pidfile_fd != -1); + ret = fstat(pidfile_fd, &st); + assert(ret == 0); + assert(S_ISREG(st.st_mode)); + n = read(pidfile_fd, pidstr, sizeof(pidstr)-1); + assert(n != -1); + pid2 = (pid_t)atoi(pidstr); + assert(pid == pid2); + close(pidfile_fd); + + ret = kill(pid, SIGUSR1); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 2); + + ret = kill(pid, SIGUSR1); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 2); + + ret = kill(pid, SIGHUP); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 4); + + ret = kill(pid, SIGHUP); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 4); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 3); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct sock_daemon_funcs test2_funcs = { + .startup = test2_startup, + .reconfigure_send = test2_reconfigure_send, + .reconfigure_recv = test2_reconfigure_recv, + .reopen_logs_send = test2_reopen_logs_send, + .reopen_logs_recv = test2_reopen_logs_recv, + .shutdown_send = test2_shutdown_send, + .shutdown_recv = test2_shutdown_recv, + }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test2", "file:", "NOTICE", + &test2_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + ret = kill(pid, SIGUSR1); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 2); + + ret = kill(pid, SIGUSR1); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 2); + + ret = kill(pid, SIGHUP); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 4); + + ret = kill(pid, SIGHUP); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 4); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 3); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); +} + +/* + * test3 + * + * Start daemon, test watching of (parent) PID + */ + +static void test3(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + pid_t pid_watch, pid, pid2; + int ret; + + pid_watch = fork(); + assert(pid_watch != -1); + + if (pid_watch == 0) { + sleep(10); + exit(0); + } + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test3", "file:", "NOTICE", + NULL, NULL, &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, NULL, false, false, pid_watch); + assert(ret == ESRCH); + + exit(0); + } + + pid2 = waitpid(pid_watch, &ret, 0); + assert(pid2 == pid_watch); + assert(WEXITSTATUS(ret) == 0); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +/* + * test4 + * + * Start daemon, test termination via wait_send function + */ + +struct test4_wait_state { +}; + +static void test4_wait_done(struct tevent_req *subreq); + +static struct tevent_req *test4_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req, *subreq; + struct test4_wait_state *state; + + req = tevent_req_create(mem_ctx, &state, struct test4_wait_state); + if (req == NULL) { + return NULL; + } + + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(10,0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test4_wait_done, req); + + return req; +} + +static void test4_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + bool status; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + + if (! status) { + tevent_req_error(req, EIO); + } else { + tevent_req_done(req); + } +} + +static bool test4_wait_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_daemon_funcs test4_funcs = { + .wait_send = test4_wait_send, + .wait_recv = test4_wait_recv, +}; + +static void test4(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + pid_t pid, pid2; + int ret; + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test4", "file:", "NOTICE", + &test4_funcs, NULL, &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == 0); + + exit(0); + } + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +/* + * test5 + * + * Start daemon, multiple client connects, requests, disconnects + */ + +#define TEST5_VALID_CLIENTS 10 +#define TEST5_MAX_CLIENTS 100 + +struct test5_pkt { + uint32_t len; + int data; +}; + +struct test5_client_state { + int id; + int fd; + bool done; +}; + +static void test5_client_callback(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test5_client_state *state = + (struct test5_client_state *)private_data; + struct test5_pkt *pkt; + ssize_t n; + int ret; + + if (buf == NULL) { + assert(buflen == 0); + + ret = 0; + } else { + assert(buflen == sizeof(struct test5_pkt)); + pkt = (struct test5_pkt *)buf; + assert(pkt->len == sizeof(struct test5_pkt)); + + ret = pkt->data; + } + + assert(state->fd != -1); + + n = write(state->fd, (void *)&ret, sizeof(int)); + assert(n == sizeof(int)); + + state->done = true; +} + +static int test5_client(const char *sockpath, int id, pid_t pid_server, + pid_t *client_pid) +{ + pid_t pid; + int fd[2]; + int ret; + ssize_t n; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct test5_client_state state; + struct sock_queue *queue; + struct test5_pkt pkt; + int conn; + + close(fd[0]); + + ev = tevent_context_init(NULL); + assert(ev != NULL); + + conn = sock_connect(sockpath); + assert(conn != -1); + + state.id = id; + state.fd = fd[1]; + state.done = false; + + queue = sock_queue_setup(ev, ev, conn, + test5_client_callback, &state); + assert(queue != NULL); + + pkt.len = 8; + pkt.data = 0xbaba; + + ret = sock_queue_write(queue, (uint8_t *)&pkt, + sizeof(struct test5_pkt)); + assert(ret == 0); + + while (! state.done) { + tevent_loop_once(ev); + } + + close(fd[1]); + state.fd = -1; + + while (kill(pid_server, 0) == 0 || errno != ESRCH) { + sleep(1); + } + exit(0); + } + + close(fd[1]); + + ret = 0; + n = read(fd[0], &ret, sizeof(ret)); + if (n == 0) { + fprintf(stderr, "client id %d read 0 bytes\n", id); + } + assert(n == 0 || n == sizeof(ret)); + + close(fd[0]); + + *client_pid = pid; + return ret; +} + +struct test5_server_state { + int num_clients; +}; + +static bool test5_connect(struct sock_client_context *client, + pid_t pid, + void *private_data) +{ + struct test5_server_state *state = + (struct test5_server_state *)private_data; + + if (state->num_clients == TEST5_VALID_CLIENTS) { + return false; + } + + state->num_clients += 1; + assert(state->num_clients <= TEST5_VALID_CLIENTS); + return true; +} + +static void test5_disconnect(struct sock_client_context *client, + void *private_data) +{ + struct test5_server_state *state = + (struct test5_server_state *)private_data; + + state->num_clients -= 1; + assert(state->num_clients >= 0); +} + +struct test5_read_state { + struct test5_pkt reply; +}; + +static void test5_read_done(struct tevent_req *subreq); + +static struct tevent_req *test5_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test5_server_state *server_state = + (struct test5_server_state *)private_data; + struct tevent_req *req, *subreq; + struct test5_read_state *state; + struct test5_pkt *pkt; + + req = tevent_req_create(mem_ctx, &state, struct test5_read_state); + assert(req != NULL); + + assert(buflen == sizeof(struct test5_pkt)); + + pkt = (struct test5_pkt *)buf; + assert(pkt->data == 0xbaba); + + state->reply.len = sizeof(struct test5_pkt); + state->reply.data = server_state->num_clients; + + subreq = sock_socket_write_send(state, ev, client, + (uint8_t *)&state->reply, + state->reply.len); + assert(subreq != NULL); + + tevent_req_set_callback(subreq, test5_read_done, req); + + return req; +} + +static void test5_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret; + bool status; + + status = sock_socket_write_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static bool test5_read_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_socket_funcs test5_client_funcs = { + .connect = test5_connect, + .disconnect = test5_disconnect, + .read_send = test5_read_send, + .read_recv = test5_read_recv, +}; + +struct test5_wait_state { +}; + +static struct tevent_req *test5_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct tevent_req *req; + struct test5_wait_state *state; + int fd = *(int *)private_data; + int ret = 1; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + close(fd); + + req = tevent_req_create(mem_ctx, &state, struct test5_wait_state); + if (req == NULL) { + return NULL; + } + + return req; +} + +static bool test5_wait_recv(struct tevent_req *req, int *perr) +{ + return true; +} + +static struct sock_daemon_funcs test5_funcs = { + .wait_send = test5_wait_send, + .wait_recv = test5_wait_recv, +}; + +static void test5(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + pid_t pid_server, pid; + int fd[2], ret, i; + ssize_t n; + pid_t client_pid[TEST5_MAX_CLIENTS]; + + pid = getpid(); + + ret = pipe(fd); + assert(ret == 0); + + pid_server = fork(); + assert(pid_server != -1); + + if (pid_server == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct test5_server_state state; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test5", "file:", "NOTICE", + &test5_funcs, &fd[1], &sockd); + assert(ret == 0); + + state.num_clients = 0; + + ret = sock_daemon_add_unix(sockd, sockpath, + &test5_client_funcs, &state); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, pid); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + close(fd[0]); + + for (i=0; i=0; i--) { + kill(client_pid[i], SIGKILL); + + pid = wait(&ret); + assert(pid != -1); + } + + ret = kill(pid_server, SIGTERM); + assert(ret == 0); + + pid = waitpid(pid_server, &ret, 0); + assert(pid == pid_server); + assert(WEXITSTATUS(ret) == 0); +} + +/* + * test6 + * + * Start daemon, test client connects, requests, replies, disconnects + */ + +struct test6_pkt { + uint32_t len; + uint32_t data; +}; + +struct test6_client_state { + bool done; +}; + +static void test6_client_callback(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test6_client_state *state = + (struct test6_client_state *)private_data; + struct test6_pkt *pkt; + + assert(buflen == sizeof(struct test6_pkt)); + pkt = (struct test6_pkt *)buf; + assert(pkt->len == sizeof(struct test6_pkt)); + assert(pkt->data == 0xffeeddcc); + + state->done = true; +} + +static void test6_client(const char *sockpath) +{ + struct tevent_context *ev; + struct test6_client_state state; + struct sock_queue *queue; + struct test6_pkt pkt; + int conn, ret; + + ev = tevent_context_init(NULL); + assert(ev != NULL); + + conn = sock_connect(sockpath); + assert(conn != -1); + + state.done = false; + + queue = sock_queue_setup(ev, ev, conn, + test6_client_callback, &state); + assert(queue != NULL); + + pkt.len = 8; + pkt.data = 0xaabbccdd; + + ret = sock_queue_write(queue, (uint8_t *)&pkt, + sizeof(struct test6_pkt)); + assert(ret == 0); + + while (! state.done) { + tevent_loop_once(ev); + } + + talloc_free(ev); +} + +struct test6_server_state { + struct sock_daemon_context *sockd; + int fd, done; +}; + +struct test6_read_state { + struct test6_server_state *server_state; + struct test6_pkt reply; +}; + +static void test6_read_done(struct tevent_req *subreq); + +static struct tevent_req *test6_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sock_client_context *client, + uint8_t *buf, size_t buflen, + void *private_data) +{ + struct test6_server_state *server_state = + (struct test6_server_state *)private_data; + struct tevent_req *req, *subreq; + struct test6_read_state *state; + struct test6_pkt *pkt; + + req = tevent_req_create(mem_ctx, &state, struct test6_read_state); + assert(req != NULL); + + state->server_state = server_state; + + assert(buflen == sizeof(struct test6_pkt)); + + pkt = (struct test6_pkt *)buf; + assert(pkt->data == 0xaabbccdd); + + state->reply.len = sizeof(struct test6_pkt); + state->reply.data = 0xffeeddcc; + + subreq = sock_socket_write_send(state, ev, client, + (uint8_t *)&state->reply, + state->reply.len); + assert(subreq != NULL); + + tevent_req_set_callback(subreq, test6_read_done, req); + + return req; +} + +static void test6_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct test6_read_state *state = tevent_req_data( + req, struct test6_read_state); + int ret; + bool status; + + status = sock_socket_write_recv(subreq, &ret); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, ret); + return; + } + + state->server_state->done = 1; + tevent_req_done(req); +} + +static bool test6_read_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_socket_funcs test6_client_funcs = { + .read_send = test6_read_send, + .read_recv = test6_read_recv, +}; + +struct test6_wait_state { + struct test6_server_state *server_state; +}; + +static void test6_wait_done(struct tevent_req *subreq); + +static struct tevent_req *test6_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + struct test6_server_state *server_state = + (struct test6_server_state *)private_data; + struct tevent_req *req, *subreq; + struct test6_wait_state *state; + ssize_t nwritten; + int ret = 1; + + nwritten = write(server_state->fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + close(server_state->fd); + server_state->fd = -1; + + req = tevent_req_create(mem_ctx, &state, struct test6_wait_state); + if (req == NULL) { + return NULL; + } + + state->server_state = (struct test6_server_state *)private_data; + + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(10,0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test6_wait_done, req); + + return req; +} + +static void test6_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct test6_wait_state *state = tevent_req_data( + req, struct test6_wait_state); + bool status; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (! status) { + tevent_req_error(req, EIO); + return; + } + + if (state->server_state->done == 0) { + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +static bool test6_wait_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_daemon_funcs test6_funcs = { + .wait_send = test6_wait_send, + .wait_recv = test6_wait_recv, +}; + +static void test6(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + pid_t pid_server, pid; + int fd[2], ret; + ssize_t n; + + pid = getpid(); + + ret = pipe(fd); + assert(ret == 0); + + pid_server = fork(); + assert(pid_server != -1); + + if (pid_server == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + struct test6_server_state server_state = { 0 }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + server_state.fd = fd[1]; + + ret = sock_daemon_setup(mem_ctx, "test6", "file:", "NOTICE", + &test6_funcs, &server_state, + &sockd); + assert(ret == 0); + + server_state.sockd = sockd; + server_state.done = 0; + + ret = sock_daemon_add_unix(sockd, sockpath, + &test6_client_funcs, &server_state); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, pid); + assert(ret == 0); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + close(fd[0]); + + test6_client(sockpath); + + pid = waitpid(pid_server, &ret, 0); + assert(pid == pid_server); + assert(WEXITSTATUS(ret) == 0); +} + +/* + * test7 + * + * Start daemon twice, confirm PID file contention + */ + +static void test7(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct sock_daemon_funcs test7_funcs; + struct stat st; + int fd[2]; + pid_t pid, pid2; + int ret; + struct tevent_context *ev; + struct sock_daemon_context *sockd; + ssize_t n; + + /* Reuse test2 funcs for the startup synchronisation */ + test7_funcs = (struct sock_daemon_funcs) { + .startup = test2_startup, + .reconfigure = test2_reconfigure, + .shutdown = test2_shutdown, + }; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test7", "file:", "NOTICE", + &test7_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + ret = stat(pidfile, &st); + assert(ret == 0); + assert(S_ISREG(st.st_mode)); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test7-parent", "file:", "NOTICE", + &test7_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EEXIST); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 3); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); +} + +/* + * test8 + * + * Start daemon, confirm that create_session argument works as expected + */ + +static void test8(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + int fd[2]; + pid_t pid, pid2, sid; + int ret; + struct tevent_context *ev; + struct sock_daemon_context *sockd; + ssize_t n; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + /* Reuse test2 funcs for the startup synchronisation */ + struct sock_daemon_funcs test8_funcs = { + .startup = test2_startup, + }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test8", "file:", "NOTICE", + &test8_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + /* create_session false above, so pid != sid */ + sid = getsid(pid); + assert(pid != sid); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + /* Reuse test2 funcs for the startup synchronisation */ + struct sock_daemon_funcs test8_funcs = { + .startup = test2_startup, + }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test8", "file:", "NOTICE", + &test8_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, true, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + /* create_session true above, so pid == sid */ + sid = getsid(pid); + assert(pid == sid); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); +} + +/* + * test9 + * + * Confirm that do_fork causes the daemon to be forked as a separate child + */ + +static void test9(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + int fd[2]; + pid_t pid, pid2; + int ret; + struct tevent_context *ev; + struct sock_daemon_context *sockd; + ssize_t n; + int pidfile_fd; + char pidstr[20] = { 0 }; + struct stat st; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + /* Reuse test2 funcs for the startup synchronisation */ + struct sock_daemon_funcs test9_funcs = { + .startup = test2_startup, + }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test9", "file:", "NOTICE", + &test9_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + /* do_fork false above, so pid should be active */ + ret = kill(pid, 0); + assert(ret == 0); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + /* Reuse test2 funcs for the startup synchronisation */ + struct sock_daemon_funcs test9_funcs = { + .startup = test2_startup, + .shutdown = test2_shutdown, + }; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test9", "file:", "NOTICE", + &test9_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, true, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + /* do_fork true above, so pid should have exited */ + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + pidfile_fd = open(pidfile, O_RDONLY, 0644); + assert(pidfile_fd != -1); + n = read(pidfile_fd, pidstr, sizeof(pidstr)-1); + assert(n != -1); + pid2 = (pid_t)atoi(pidstr); + assert(pid != pid2); + close(pidfile_fd); + + ret = kill(pid2, SIGTERM); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 3); + + /* + * pid2 isn't our child, so can't call waitpid(). kill(pid2, 0) + * is unreliable - pid2 may have been recycled. Above indicates + * that the shutdown function was called, so just do 1 final + * check to see if pidfile has been removed. + */ + ret = stat(sockpath, &st); + assert(ret == -1); + + close(fd[0]); +} + +static void test10_shutdown(void *private_data) +{ + int fd = *(int *)private_data; + int ret = 3; + ssize_t nwritten; + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); +} + +struct test10_wait_state { +}; + +static void test10_wait_done(struct tevent_req *subreq); + +static struct tevent_req *test10_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *private_data) +{ + int fd = *(int *)private_data; + struct tevent_req *req, *subreq; + struct test10_wait_state *state; + size_t nwritten; + int ret = 1; + + req = tevent_req_create(mem_ctx, &state, struct test10_wait_state); + if (req == NULL) { + return NULL; + } + + subreq = tevent_wakeup_send(state, ev, + tevent_timeval_current_ofs(10, 0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test10_wait_done, req); + + nwritten = write(fd, &ret, sizeof(ret)); + assert(nwritten == sizeof(ret)); + + return req; +} + +static void test10_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + bool status; + + status = tevent_wakeup_recv(subreq); + if (! status) { + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +static bool test10_wait_recv(struct tevent_req *req, int *perr) +{ + int ret; + + if (tevent_req_is_unix_error(req, &ret)) { + if (perr != NULL) { + *perr = ret; + } + return false; + } + + return true; +} + +static struct sock_daemon_funcs test10_funcs = { + .shutdown = test10_shutdown, + .wait_send = test10_wait_send, + .wait_recv = test10_wait_recv, +}; + +/* + * test10 + * + * Confirm that the daemon starts successfully if there is a stale socket + */ + +static void test10(TALLOC_CTX *mem_ctx, const char *pidfile, + const char *sockpath) +{ + struct stat st; + int fd[2]; + pid_t pid, pid2; + int ret; + ssize_t n; + int pidfile_fd; + char pidstr[20] = { 0 }; + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test10", "file:", "NOTICE", + &test10_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + /* KILL will leave PID file and socket behind */ + ret = kill (pid, SIGKILL); + assert(ret == 0); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + ret = stat(sockpath, &st); + assert(ret == 0); + + close(fd[0]); + + ret = pipe(fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + struct tevent_context *ev; + struct sock_daemon_context *sockd; + + close(fd[0]); + + ev = tevent_context_init(mem_ctx); + assert(ev != NULL); + + ret = sock_daemon_setup(mem_ctx, "test10", "file:", "NOTICE", + &test10_funcs, &fd[1], &sockd); + assert(ret == 0); + + ret = sock_daemon_add_unix(sockd, sockpath, + &dummy_socket_funcs, NULL); + assert(ret == 0); + + ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1); + assert(ret == EINTR); + + exit(0); + } + + close(fd[1]); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 1); + + pidfile_fd = open(pidfile, O_RDONLY, 0644); + assert(pidfile_fd != -1); + n = read(pidfile_fd, pidstr, sizeof(pidstr)-1); + assert(n != -1); + pid2 = (pid_t)atoi(pidstr); + assert(pid == pid2); + close(pidfile_fd); + + ret = kill(pid, SIGTERM); + assert(ret == 0); + + n = read(fd[0], &ret, sizeof(ret)); + assert(n == sizeof(ret)); + assert(ret == 3); + + pid2 = waitpid(pid, &ret, 0); + assert(pid2 == pid); + assert(WEXITSTATUS(ret) == 0); + + close(fd[0]); + + ret = stat(pidfile, &st); + assert(ret == -1); + + ret = stat(sockpath, &st); + assert(ret == -1); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + const char *pidfile, *sockpath; + int num; + + if (argc != 4) { + fprintf(stderr, "%s \n", argv[0]); + exit(1); + } + + pidfile = argv[1]; + sockpath = argv[2]; + num = atoi(argv[3]); + + mem_ctx = talloc_new(NULL); + assert(mem_ctx != NULL); + + switch (num) { + case 1: + test1(mem_ctx, pidfile, sockpath); + break; + + case 2: + test2(mem_ctx, pidfile, sockpath); + break; + + case 3: + test3(mem_ctx, pidfile, sockpath); + break; + + case 4: + test4(mem_ctx, pidfile, sockpath); + break; + + case 5: + test5(mem_ctx, pidfile, sockpath); + break; + + case 6: + test6(mem_ctx, pidfile, sockpath); + break; + + case 7: + test7(mem_ctx, pidfile, sockpath); + break; + + case 8: + test8(mem_ctx, pidfile, sockpath); + break; + + case 9: + test9(mem_ctx, pidfile, sockpath); + break; + + case 10: + test10(mem_ctx, pidfile, sockpath); + break; + + default: + fprintf(stderr, "Unknown test number %d\n", num); + } + + return 0; +} -- cgit v1.2.3