Hi,

On top of those patches, here a 3 more patches.
The first one makes the systemd wrapper check for a HAPROXY_STATS_SOCKET
environment variable. If set, it will use that as an argument to -x, when
reloading the process.
The second one sends listening unix sockets, as well as IPv4/v6 sockets. 
I see no reason not to, and that means we no longer have to wait until
the old process close the socket before being able to accept new connections
on it.
The third one adds a new global optoin, nosockettransfer, if set, we assume
we will never try to transfer listening sockets through the stats socket,
and close any socket nout bound to our process, to save a few file
descriptors.

Regards,

Olivier
>From 8d6c38b6824346b096ba31757ab62bc986a433b3 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Sun, 9 Apr 2017 16:28:10 +0200
Subject: [PATCH 7/9] MINOR: systemd wrapper: add support for passing the -x
 option.

Make the systemd wrapper chech if HAPROXY_STATS_SOCKET if set.
If set, it will use it as an argument to the "-x" option, which makes
haproxy asks for any listening socket, on the stats socket, in order
to achieve reloads with no new connection lost.
---
 contrib/systemd/haproxy.service.in |  2 ++
 src/haproxy-systemd-wrapper.c      | 10 +++++++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/contrib/systemd/haproxy.service.in 
b/contrib/systemd/haproxy.service.in
index dca81a2..05bb716 100644
--- a/contrib/systemd/haproxy.service.in
+++ b/contrib/systemd/haproxy.service.in
@@ -3,6 +3,8 @@ Description=HAProxy Load Balancer
 After=network.target
 
 [Service]
+# You can point the environment variable HAPROXY_STATS_SOCKET to a stats
+# socket if you want seamless reloads.
 Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid"
 ExecStartPre=@SBINDIR@/haproxy -f $CONFIG -c -q
 ExecStart=@SBINDIR@/haproxy-systemd-wrapper -f $CONFIG -p $PIDFILE
diff --git a/src/haproxy-systemd-wrapper.c b/src/haproxy-systemd-wrapper.c
index f6a9c85..1d00111 100644
--- a/src/haproxy-systemd-wrapper.c
+++ b/src/haproxy-systemd-wrapper.c
@@ -92,11 +92,15 @@ static void spawn_haproxy(char **pid_strv, int nb_pid)
        pid = fork();
        if (!pid) {
                char **argv;
+               char *stats_socket = NULL;
                int i;
                int argno = 0;
 
                /* 3 for "haproxy -Ds -sf" */
-               argv = calloc(4 + main_argc + nb_pid + 1, sizeof(char *));
+               if (nb_pid > 0)
+                       stats_socket = getenv("HAPROXY_STATS_SOCKET");
+               argv = calloc(4 + main_argc + nb_pid + 1 +
+                   stats_socket != NULL ? 2 : 0, sizeof(char *));
                if (!argv) {
                        fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: 
failed to calloc(), please try again later.\n");
                        exit(1);
@@ -121,6 +125,10 @@ static void spawn_haproxy(char **pid_strv, int nb_pid)
                        argv[argno++] = "-sf";
                        for (i = 0; i < nb_pid; ++i)
                                argv[argno++] = pid_strv[i];
+                       if (stats_socket != NULL) {
+                               argv[argno++] = "-x";
+                               argv[argno++] = stats_socket;
+                       }
                }
                argv[argno] = NULL;
 
-- 
2.9.3

>From df5e6e70f2e73fca9e28ba273904ab5c5acf53d3 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Sun, 9 Apr 2017 19:17:15 +0200
Subject: [PATCH 8/9] MINOR: cli: When sending listening sockets, send unix
 sockets too.

Send unix sockets, as well as IPv4/IPv6 sockets, so that we don't have to
wait for the old process to die before being able to bind those.
---
 src/cli.c        |  6 ++++--
 src/proto_uxst.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/src/cli.c b/src/cli.c
index d5ff11f..533f792 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -1067,7 +1067,8 @@ static int _getsocks(char **args, struct appctx *appctx, 
void *private)
                list_for_each_entry(l, &px->conf.listeners, by_fe) {
                        /* Only transfer IPv4/IPv6 sockets */
                        if (l->proto->sock_family == AF_INET ||
-                           l->proto->sock_family == AF_INET6)
+                           l->proto->sock_family == AF_INET6 ||
+                           l->proto->sock_family == AF_UNIX)
                                tot_fd_nb++;
                }
                px = px->next;
@@ -1120,7 +1121,8 @@ static int _getsocks(char **args, struct appctx *appctx, 
void *private)
                        /* Only transfer IPv4/IPv6 sockets */
                        if (l->state >= LI_LISTEN &&
                            (l->proto->sock_family == AF_INET ||
-                           l->proto->sock_family == AF_INET6)) {
+                           l->proto->sock_family == AF_INET6 ||
+                           l->proto->sock_family == AF_UNIX)) {
                                memcpy(&tmpfd[i % MAX_SEND_FD], &l->fd, 
sizeof(l->fd));
                                if (!l->netns)
                                        tmpbuf[curoff++] = 0;
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 27ff0fa..d68267e 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -150,6 +150,54 @@ static void destroy_uxst_socket(const char *path)
  ********************************/
 
 
+static int uxst_find_compatible_fd(struct listener *l)
+{
+       struct xfer_sock_list *xfer_sock = xfer_sock_list;
+       int ret = -1;
+
+       while (xfer_sock) {
+               struct sockaddr_un *un1 = (void *)&l->addr;
+               struct sockaddr_un *un2 = (void *)&xfer_sock->addr;
+
+               /*
+                * The bound socket's path as returned by getsockaddr
+                * will be the temporary name <sockname>.XXXXX.tmp,
+                * so we can't just compare the two names
+                */
+               if (xfer_sock->addr.ss_family == AF_UNIX &&
+                   strncmp(un1->sun_path, un2->sun_path,
+                   strlen(un1->sun_path)) == 0) {
+                       char *after_sockname = un2->sun_path +
+                           strlen(un1->sun_path);
+                       /* Make a reasonnable effort to check that
+                        * it is indeed a haproxy-generated temporary
+                        * name, it's not perfect, but probably good enough.
+                        */
+                       if (after_sockname[0] == '.') {
+                               after_sockname++;
+                               while (after_sockname[0] >= '0' &&
+                                   after_sockname[0] <= '9')
+                                       after_sockname++;
+                               if (!strcmp(after_sockname, ".tmp"))
+                                       break;
+                       }
+               }
+               xfer_sock = xfer_sock->next;
+       }
+       if (xfer_sock != NULL) {
+               ret = xfer_sock->fd;
+               if (xfer_sock == xfer_sock_list)
+                       xfer_sock_list = xfer_sock->next;
+               if (xfer_sock->prev)
+                       xfer_sock->prev->next = xfer_sock->next;
+               if (xfer_sock->next)
+                       xfer_sock->next->prev = xfer_sock->next->prev;
+               free(xfer_sock);
+       }
+       return ret;
+
+}
+
 /* This function creates a UNIX socket associated to the listener. It changes
  * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling.
  * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. It
@@ -179,6 +227,8 @@ static int uxst_bind_listener(struct listener *listener, 
char *errmsg, int errle
        if (listener->state != LI_ASSIGNED)
                return ERR_NONE; /* already bound */
                
+       if (listener->fd == -1)
+               listener->fd = uxst_find_compatible_fd(listener);
        path = ((struct sockaddr_un *)&listener->addr)->sun_path;
 
        /* if the listener already has an fd assigned, then we were offered the
-- 
2.9.3

>From e81ed243f466f5d1e9850ee31ca6bba5d6af6f63 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Sun, 9 Apr 2017 23:39:52 +0200
Subject: [PATCH 9/9] MINOR: global: Add a new option to close unused sockets.

By default, processes keep all bound socket opened, even those that are only
used by other processes, so that we can provide all sockets when asked for
them.
However it may be of no use for people who don't use socket transfer, so
provide a new global option, "nosockettransfer", that restore the old
behavior of closing the sockets not belonging to the process.
---
 doc/configuration.txt  | 7 +++++++
 include/types/global.h | 2 ++
 src/cfgparse.c         | 5 +++++
 src/haproxy.c          | 9 +++++++--
 4 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 05f0701..c18821e 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -587,6 +587,7 @@ The following keywords are supported in the "global" 
section :
    - nosplice
    - nogetaddrinfo
    - noreuseport
+   - nosockettransfer
    - spread-checks
    - server-state-base
    - server-state-file
@@ -1250,6 +1251,12 @@ noreuseport
   Disables the use of SO_REUSEPORT - see socket(7). It is equivalent to the
   command line argument "-dR".
 
+nosockettrasnfer
+  By default, each haproxy process keeps all sockets opened, event those that
+  are only used by another processes, so that any process can provide all the
+  sockets, to make reloads seamless. This option disables this, and close all
+  unused sockets, to save some file descriptors.
+
 spread-checks <0..50, in percent>
   Sometimes it is desirable to avoid sending agent and health checks to
   servers at exact intervals, for instance when many logical servers are
diff --git a/include/types/global.h b/include/types/global.h
index df8e2c6..57b969d 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -62,6 +62,8 @@
 #define GTUNE_USE_REUSEPORT      (1<<6)
 #define GTUNE_RESOLVE_DONTFAIL   (1<<7)
 
+#define GTUNE_SOCKET_TRANSFER   (1<<8)
+
 /* Access level for a stats socket */
 #define ACCESS_LVL_NONE     0
 #define ACCESS_LVL_USER     1
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e1b6b3e..0f9c15a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -659,6 +659,11 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
                        goto out;
                global.tune.options &= ~GTUNE_USE_REUSEPORT;
        }
+       else if (!strcmp(args[0], "nosockettransfer")) {
+               if (alertif_too_many_args(0, file, linenum, args, &err_code))
+                       goto out;
+               global.tune.options &= ~GTUNE_SOCKET_TRANSFER;
+       }
        else if (!strcmp(args[0], "quiet")) {
                if (alertif_too_many_args(0, file, linenum, args, &err_code))
                        goto out;
diff --git a/src/haproxy.c b/src/haproxy.c
index af86b78..0e3350c 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -864,6 +864,7 @@ static void init(int argc, char **argv)
 #if defined(SO_REUSEPORT)
        global.tune.options |= GTUNE_USE_REUSEPORT;
 #endif
+       global.tune.options |= GTUNE_SOCKET_TRANSFER;
 
        pid = getpid();
        progname = *argv;
@@ -2164,8 +2165,12 @@ int main(int argc, char **argv)
                px = proxy;
                while (px != NULL) {
                        if (px->bind_proc && px->state != PR_STSTOPPED) {
-                               if (!(px->bind_proc & (1UL << proc)))
-                                       zombify_proxy(px);
+                               if (!(px->bind_proc & (1UL << proc))) {
+                                       if (global.tune.options & 
GTUNE_SOCKET_TRANSFER)
+                                               zombify_proxy(px);
+                                       else
+                                               stop_proxy(px);
+                               }
                        }
                        px = px->next;
                }
-- 
2.9.3

Reply via email to