From 3ff6ac2e782cdfe1d32240556478a2b5a3b8c057 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Wed, 10 Jun 2020 00:27:53 +0200 Subject: Make server stop/start exec asynchronous --- server.c | 106 +++++++++++++++++++++++++++++++++++++++++++++------------------ server.h | 1 + uring.c | 37 ++++++++++++++++++++++ uring.h | 5 +++ 4 files changed, 119 insertions(+), 30 deletions(-) diff --git a/server.c b/server.c index c3bb433..7ec0804 100644 --- a/server.c +++ b/server.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include #include #include @@ -5,7 +6,10 @@ #include #include #include -#include +#include +#include +#include +#include #include "main.h" #include "uring.h" @@ -87,6 +91,8 @@ server_delete(struct cfg *cfg, struct server *scfg) uring_cancel(cfg, &local->task); } + uring_poll_cancel(cfg, &scfg->exec_task); + uring_task_put(cfg, &scfg->exec_task); uring_task_put(cfg, &scfg->task); } @@ -229,42 +235,81 @@ out: return false; } +static void +server_exec_free(struct uring_task *task) +{ + //struct server *scfg = container_of(task, struct server, exec_task); + + fprintf(stderr, "%s called\n", __func__); +} + +#ifndef P_PIDFD +#define P_PIDFD 3 +#endif + +static void +server_exec_done(struct cfg *cfg, struct uring_task *task, int res) +{ + struct server *scfg = container_of(task, struct server, exec_task); + int r; + siginfo_t info; + + if (task->dead) + goto out; + + if (!(res & POLLIN)) { + fprintf(stderr, "%s: unexpected result: %i\n", __func__, res); + goto out; + } + + r = waitid(P_PIDFD, scfg->exec_task.fd, &info, WEXITED); + if (r < 0) { + perror("waitid"); + goto out; + } + + fprintf(stderr, "Command executed, return value: %i\n", info.si_status); +out: + uring_task_close_fd(cfg, &scfg->exec_task); +} + +static int +server_exec_child(void *ptr) +{ + const char *cmd = ptr; + + execl(cmd, cmd, NULL); + return errno; +} + +#ifndef CLONE_PIDFD +#define CLONE_PIDFD 0x00001000 +#endif + static bool -exec_cmd(struct cfg *cfg, struct server *scfg, const char *cmd) +server_exec(struct cfg *cfg, struct server *scfg, const char *cmd) { - pid_t pid, wpid; - int wstatus; + char stack[4096]; /* Beautiful/horrible hack :) */ + int pidfd; + int r; if (!cfg || !scfg || !cmd) return false; - pid = fork(); - if (pid < 0) { - perror("fork"); + if (scfg->exec_task.fd >= 0) return false; - } else if (pid > 0) { - do { - wpid = waitpid(pid, &wstatus, 0); - if (wpid < 0) { - perror("waitpid"); - return false; - } - } while (!WIFEXITED(wstatus)); - - fprintf(stderr, "Child %s exited: %i\n", cmd, - WEXITSTATUS(wstatus)); - - if (WEXITSTATUS(wstatus) == 0) - return true; - else - return false; - - } else { - execl(cmd, cmd, NULL); - perror("execl"); - exit(EXIT_FAILURE); + r = clone(server_exec_child, stack + sizeof(stack), + CLONE_VM | CLONE_VFORK | CLONE_PIDFD | SIGCHLD, + (void *)cmd, &pidfd); + if (r < 0) { + perror("clone"); + return false; } + + uring_task_set_fd(&scfg->exec_task, pidfd); + uring_poll(cfg, &scfg->exec_task, POLLIN, server_exec_done); + return true; } bool @@ -276,7 +321,7 @@ server_start(struct cfg *cfg, struct server *scfg) switch (scfg->start_method) { case SERVER_START_METHOD_EXEC: - return exec_cmd(cfg, scfg, scfg->start_exec); + return server_exec(cfg, scfg, scfg->start_exec); case SERVER_START_METHOD_UNDEFINED: default: @@ -295,7 +340,7 @@ server_stop(struct cfg *cfg, struct server *scfg) switch (scfg->stop_method) { case SERVER_STOP_METHOD_EXEC: - return exec_cmd(cfg, scfg, scfg->stop_exec); + return server_exec(cfg, scfg, scfg->stop_exec); case SERVER_STOP_METHOD_RCON: rcon_init(cfg, scfg); @@ -558,6 +603,7 @@ server_new(struct cfg *cfg, const char *name) scfg->stop_method = SERVER_STOP_METHOD_UNDEFINED; scfg->start_method = SERVER_START_METHOD_UNDEFINED; uring_task_init(&scfg->task, "scfg", uring_parent(cfg), server_free); + uring_task_init(&scfg->exec_task, "exec", &scfg->task, server_exec_free); list_init(&scfg->remotes); list_init(&scfg->locals); list_init(&scfg->proxys); diff --git a/server.h b/server.h index 43c5b9e..495f7c4 100644 --- a/server.h +++ b/server.h @@ -52,6 +52,7 @@ struct server { unsigned idle_timeout; unsigned idle_count; + struct uring_task exec_task; struct uring_task task; struct list_head list; }; diff --git a/uring.c b/uring.c index 20d8234..9dcdcf6 100644 --- a/uring.c +++ b/uring.c @@ -256,6 +256,43 @@ uring_accept(struct cfg *cfg, struct uring_task *task, struct sockaddr_in46 *add io_uring_sqe_set_data(sqe, task); } +void +uring_poll(struct cfg *cfg, struct uring_task *task, short poll_mask, callback_t callback) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + if (!sqe) + perrordie("io_uring_sqe"); + + if (task->fd < 0) { + error("uring_poll called with no fd set\n"); + return; + } + + uring_task_get(cfg, task); + task->callback = callback; + io_uring_prep_poll_add(sqe, task->fd, poll_mask); + io_uring_sqe_set_data(sqe, task); +} + +void +uring_poll_cancel(struct cfg *cfg, struct uring_task *task) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + if (!sqe) + perrordie("io_uring_sqe"); + + if (task->fd < 0) { + error("uring_poll_cancel called with no fd set\n"); + return; + } + + task->dead = true; + io_uring_prep_poll_remove(sqe, task); + io_uring_sqe_set_data(sqe, NULL); +} + void uring_cancel(struct cfg *cfg, struct uring_task *task) { diff --git a/uring.h b/uring.h index ff6bfb9..83119a1 100644 --- a/uring.h +++ b/uring.h @@ -38,6 +38,11 @@ void uring_connect(struct cfg *cfg, struct uring_task *task, void uring_accept(struct cfg *cfg, struct uring_task *task, struct sockaddr_in46 *addr, callback_t callback); +void uring_poll(struct cfg *cfg, struct uring_task *task, short poll_mask, + callback_t callback); + +void uring_poll_cancel(struct cfg *cfg, struct uring_task *task); + void uring_cancel(struct cfg *cfg, struct uring_task *task); void uring_refdump(struct uring_ev *uev); -- cgit v1.2.3