]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: proto: duplicate receivers marked RX_F_MUST_DUP
authorWilly Tarreau <w@1wt.eu>
Mon, 27 Feb 2023 15:39:32 +0000 (16:39 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 21 Apr 2023 15:41:26 +0000 (17:41 +0200)
The different protocol's ->bind() function will now check the receiver's
RX_F_MUST_DUP flag to decide whether to bind a fresh new listener from
scratch or reuse an existing one and just duplicate it. It turns out
that the existing code already supports reusing FDs since that was done
as part of the FD passing and inheriting mechanism. Here it's not much
different, we pass the FD of the reference receiver, it gets duplicated
and becomes the new receiver's FD.

These FDs are also marked RX_F_INHERITED so that they are not exported
and avoid being touched directly (only the reference should be touched).

src/proto_sockpair.c
src/sock_inet.c
src/sock_unix.c

index 1c5d7422f715aefd5dabaddb0b0e2a781c21ae3f..a719063a81d94bb408abb6688f1e1370b9e16b24 100644 (file)
@@ -137,6 +137,30 @@ int sockpair_bind_receiver(struct receiver *rx, char **errmsg)
        if (rx->flags & RX_F_BOUND)
                return ERR_NONE;
 
+       if (rx->flags & RX_F_MUST_DUP) {
+               /* this is a secondary receiver that is an exact copy of a
+                * reference which must already be bound (or has failed).
+                * We'll try to dup() the other one's FD and take it. We
+                * try hard not to reconfigure the socket since it's shared.
+                */
+               BUG_ON(!rx->shard_info);
+               if (!(rx->shard_info->ref->flags & RX_F_BOUND)) {
+                       /* it's assumed that the first one has already reported
+                        * the error, let's not spam with another one, and do
+                        * not set ERR_ALERT.
+                        */
+                       err |= ERR_RETRYABLE;
+                       goto bind_ret_err;
+               }
+               /* taking the other one's FD will result in it being marked
+                * extern and being dup()ed. Let's mark the receiver as
+                * inherited so that it properly bypasses all second-stage
+                * setup and avoids being passed to new processes.
+                */
+               rx->flags |= RX_F_INHERITED;
+               rx->fd = rx->shard_info->ref->fd;
+       }
+
        if (rx->fd == -1) {
                err |= ERR_FATAL | ERR_ALERT;
                memprintf(errmsg, "sockpair may be only used with inherited FDs");
@@ -164,6 +188,7 @@ int sockpair_bind_receiver(struct receiver *rx, char **errmsg)
        if (errmsg && *errmsg)
                memprintf(errmsg, "%s for [fd %d]", *errmsg, rx->fd);
 
+ bind_ret_err:
        return err;
 
  bind_close_return:
index f85582cfdea9330e3a6feed4149346d7ee876e89..b7861f78859076e2e31302bf09e1205312ca10d4 100644 (file)
@@ -288,6 +288,30 @@ int sock_inet_bind_receiver(struct receiver *rx, char **errmsg)
        if (rx->flags & RX_F_BOUND)
                return ERR_NONE;
 
+       if (rx->flags & RX_F_MUST_DUP) {
+               /* this is a secondary receiver that is an exact copy of a
+                * reference which must already be bound (or has failed).
+                * We'll try to dup() the other one's FD and take it. We
+                * try hard not to reconfigure the socket since it's shared.
+                */
+               BUG_ON(!rx->shard_info);
+               if (!(rx->shard_info->ref->flags & RX_F_BOUND)) {
+                       /* it's assumed that the first one has already reported
+                        * the error, let's not spam with another one, and do
+                        * not set ERR_ALERT.
+                        */
+                       err |= ERR_RETRYABLE;
+                       goto bind_ret_err;
+               }
+               /* taking the other one's FD will result in it being marked
+                * extern and being dup()ed. Let's mark the receiver as
+                * inherited so that it properly bypasses all second-stage
+                * setup and avoids being passed to new processes.
+                */
+               rx->flags |= RX_F_INHERITED;
+               rx->fd = rx->shard_info->ref->fd;
+       }
+
        /* if no FD was assigned yet, we'll have to either find a compatible
         * one or create a new one.
         */
@@ -421,6 +445,7 @@ int sock_inet_bind_receiver(struct receiver *rx, char **errmsg)
                addr_to_str(&addr_inet, pn, sizeof(pn));
                memprintf(errmsg, "%s for [%s:%d]", *errmsg, pn, get_host_port(&addr_inet));
        }
+ bind_ret_err:
        return err;
 
  bind_close_return:
index ac3cb642d7c51f8b5d92710661be48b0474fd07d..1f7da504dfd5e2a1f8fda72b8d3d3d8f1453fc24 100644 (file)
@@ -154,6 +154,30 @@ int sock_unix_bind_receiver(struct receiver *rx, char **errmsg)
        if (rx->flags & RX_F_BOUND)
                return ERR_NONE;
 
+       if (rx->flags & RX_F_MUST_DUP) {
+               /* this is a secondary receiver that is an exact copy of a
+                * reference which must already be bound (or has failed).
+                * We'll try to dup() the other one's FD and take it. We
+                * try hard not to reconfigure the socket since it's shared.
+                */
+               BUG_ON(!rx->shard_info);
+               if (!(rx->shard_info->ref->flags & RX_F_BOUND)) {
+                       /* it's assumed that the first one has already reported
+                        * the error, let's not spam with another one, and do
+                        * not set ERR_ALERT.
+                        */
+                       err |= ERR_RETRYABLE;
+                       goto bind_ret_err;
+               }
+               /* taking the other one's FD will result in it being marked
+                * extern and being dup()ed. Let's mark the receiver as
+                * inherited so that it properly bypasses all second-stage
+                * setup and avoids being passed to new processes.
+                */
+               rx->flags |= RX_F_INHERITED;
+               rx->fd = rx->shard_info->ref->fd;
+       }
+
        /* if no FD was assigned yet, we'll have to either find a compatible
         * one or create a new one.
         */
@@ -347,6 +371,7 @@ int sock_unix_bind_receiver(struct receiver *rx, char **errmsg)
                else
                        memprintf(errmsg, "%s [fd %d]", *errmsg, fd);
        }
+ bind_ret_err:
        return err;
 
  bind_close_return: