]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mworker/cli: keep the connection of the FD that ask for a reload
authorWilliam Lallemand <wlallemand@haproxy.org>
Thu, 22 Sep 2022 15:26:23 +0000 (17:26 +0200)
committerWilliam Lallemand <wlallemand@haproxy.org>
Thu, 22 Sep 2022 16:16:19 +0000 (18:16 +0200)
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.

src/haproxy.c
src/mworker.c

index ae2f5ebf30088319d3fd0ec89a2e2fb07b1cf393..a837f48b90c572fc59c5b6a56bf570f73ce4e9a5 100644 (file)
@@ -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);
                }
        }
 
index 6365a59cf055c86ac7cad72685cf919964270c7a..0f9314430209dee8e35c2229e1129623091e8daf 100644 (file)
 #define _GNU_SOURCE
 
 #include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #if defined(USE_SYSTEMD)
 #include <systemd/sd-daemon.h>
@@ -32,6 +34,7 @@
 #include <haproxy/listener.h>
 #include <haproxy/mworker.h>
 #include <haproxy/peers.h>
+#include <haproxy/proto_sockpair.h>
 #include <haproxy/proxy.h>
 #include <haproxy/sc_strm.h>
 #include <haproxy/signal.h>
@@ -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;