From: William Lallemand Date: Thu, 22 Sep 2022 15:26:23 +0000 (+0200) Subject: MEDIUM: mworker/cli: keep the connection of the FD that ask for a reload X-Git-Tag: v2.7-dev7~39 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec059c249e3ab8512bea14628e0032c36b89a80b;p=thirdparty%2Fhaproxy.git MEDIUM: mworker/cli: keep the connection of the FD that ask for a reload When using the "reload" command over the master CLI, all connections to the master CLI were cut, this was unfortunate because it could have been used to implement a synchronous reload command. This patch implements an architecture to keep the connection alive after the reload. The master CLI is now equipped with a listener which uses a socketpair, the 2 FDs of this socketpair are stored in the mworker_proc of the master, which the master keeps via the environment variable. ipc_fd[1] is used as a listener for the master CLI. During the "reload" command, the CLI will send the FD of the current session over ipc_fd[0], then the reload is achieved, so the master won't handle the recv of the FD. Once reloaded, ipc_fd[1] receives the FD of the session, so the connection is preserved. Of course it is a new context, so everything like the "prompt mode" are lost. Only the FD which performs the reload is kept. --- diff --git a/src/haproxy.c b/src/haproxy.c index ae2f5ebf30..a837f48b90 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2061,6 +2061,19 @@ static void init(int argc, char **argv) tmproc->options |= PROC_O_TYPE_MASTER; /* master */ tmproc->pid = pid; tmproc->timestamp = start_date.tv_sec; + + /* Creates the mcli_reload listener, which is the listener used + * to retrieve the master CLI session which asked for the reload. + * + * ipc_fd[1] will be used as a listener, and ipc_fd[0] + * will be used to send the FD of the session. + * + * Both FDs will be kept in the master. + */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, tmproc->ipc_fd) < 0) { + ha_alert("cannot create the mcli_reload socketpair.\n"); + exit(EXIT_FAILURE); + } proc_self = tmproc; LIST_APPEND(&proc_list, &tmproc->list); @@ -2098,6 +2111,7 @@ static void init(int argc, char **argv) } if (!LIST_ISEMPTY(&mworker_cli_conf)) { + char *path = NULL; if (mworker_cli_proxy_create() < 0) { ha_alert("Can't create the master's CLI.\n"); @@ -2114,6 +2128,14 @@ static void init(int argc, char **argv) free(c->s); free(c); } + /* Create the mcli_reload listener from the proc_self struct */ + memprintf(&path, "sockpair@%d", proc_self->ipc_fd[1]); + + if (mworker_cli_proxy_new_listener(path) < 0) { + ha_alert("Cannot create the mcli_reload listener.\n"); + exit(EXIT_FAILURE); + } + ha_free(&path); } } diff --git a/src/mworker.c b/src/mworker.c index 6365a59cf0..0f93144302 100644 --- a/src/mworker.c +++ b/src/mworker.c @@ -13,10 +13,12 @@ #define _GNU_SOURCE #include +#include #include #include #include #include +#include #if defined(USE_SYSTEMD) #include @@ -32,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -126,7 +129,7 @@ void mworker_proc_list_to_env() minreloads = child->reloads; if (child->pid > -1) - memprintf(&msg, "%s|type=%c;fd=%d;pid=%d;reloads=%d;failedreloads=%d;timestamp=%d;id=%s;version=%s", msg ? msg : "", type, child->ipc_fd[0], child->pid, child->reloads, child->failedreloads, child->timestamp, child->id ? child->id : "", child->version); + memprintf(&msg, "%s|type=%c;fd=%d;cfd=%d;pid=%d;reloads=%d;failedreloads=%d;timestamp=%d;id=%s;version=%s", msg ? msg : "", type, child->ipc_fd[0], child->ipc_fd[1], child->pid, child->reloads, child->failedreloads, child->timestamp, child->id ? child->id : "", child->version); } if (msg) setenv("HAPROXY_PROCESSES", msg, 1); @@ -203,6 +206,8 @@ int mworker_env_to_proc_list() } else if (strncmp(subtoken, "fd=", 3) == 0) { child->ipc_fd[0] = atoi(subtoken+3); + } else if (strncmp(subtoken, "cfd=", 4) == 0) { + child->ipc_fd[1] = atoi(subtoken+4); } else if (strncmp(subtoken, "pid=", 4) == 0) { child->pid = atoi(subtoken+4); } else if (strncmp(subtoken, "reloads=", 8) == 0) { @@ -623,9 +628,29 @@ static int cli_io_handler_show_proc(struct appctx *appctx) /* reload the master process */ static int cli_parse_reload(char **args, char *payload, struct appctx *appctx, void *private) { + struct stconn *scb = NULL; + struct stream *strm = NULL; + struct connection *conn = NULL; + int fd = -1; + if (!cli_has_level(appctx, ACCESS_LVL_OPER)) return 1; + /* This ask for a synchronous reload, which means we will keep this FD + instead of closing it. */ + + scb = appctx_sc(appctx); + if (scb) + strm = sc_strm(scb); + if (strm && strm->scf) + conn = sc_conn(strm->scf); + if (conn) + fd = conn_fd(conn); + + /* Send the FD of the current session to the "cli_reload" FD, which won't be polled */ + if (fd != -1 && send_fd_uxst(proc_self->ipc_fd[0], fd) == 0) { + close(fd); /* avoid the leak of the FD after sending it via the socketpair */ + } mworker_reload(); return 1;