8be66a
From 262544a451c11c38e92c45047ec2adeaeb2a0a7e Mon Sep 17 00:00:00 2001
8be66a
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
8be66a
Date: Thu, 20 Aug 2020 13:00:37 +0200
8be66a
Subject: [PATCH] socket: New option 'FlushPending' (boolean) to flush socket
8be66a
 before entering listening state
8be66a
8be66a
Disabled by default. When Enabled, before listening on the socket, flush the content.
8be66a
Applies when Accept=no only.
8be66a
8be66a
(cherry picked from commit 3e5f04bf6468fcb79c080f02b0eab08f258bff0c)
8be66a
8be66a
Resolves: #1870638
8be66a
---
8be66a
 doc/TRANSIENT-SETTINGS.md             |  1 +
8be66a
 man/systemd.socket.xml                | 12 ++++++++++++
8be66a
 src/core/dbus-socket.c                |  4 ++++
8be66a
 src/core/load-fragment-gperf.gperf.m4 |  1 +
8be66a
 src/core/socket.c                     | 11 +++++++++++
8be66a
 src/core/socket.h                     |  1 +
8be66a
 src/shared/bus-unit-util.c            |  3 ++-
8be66a
 7 files changed, 32 insertions(+), 1 deletion(-)
8be66a
8be66a
diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md
8be66a
index 1a4e79190a..995b8797ef 100644
8be66a
--- a/doc/TRANSIENT-SETTINGS.md
8be66a
+++ b/doc/TRANSIENT-SETTINGS.md
8be66a
@@ -388,6 +388,7 @@ Most socket unit settings are available to transient units.
8be66a
 ✓ SocketMode=
8be66a
 ✓ DirectoryMode=
8be66a
 ✓ Accept=
8be66a
+✓ FlushPending=
8be66a
 ✓ Writable=
8be66a
 ✓ MaxConnections=
8be66a
 ✓ MaxConnectionsPerSource=
8be66a
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
8be66a
index 19c2ca9907..8676b4e03f 100644
8be66a
--- a/man/systemd.socket.xml
8be66a
+++ b/man/systemd.socket.xml
8be66a
@@ -425,6 +425,18 @@
8be66a
         false, in read-only mode. Defaults to false.</para></listitem>
8be66a
       </varlistentry>
8be66a
 
8be66a
+      <varlistentry>
8be66a
+        <term><varname>FlushPending=</varname></term>
8be66a
+        <listitem><para>Takes a boolean argument. May only be used when
8be66a
+        <option>Accept=no</option>. If yes, the socket's buffers are cleared after the
8be66a
+        triggered service exited. This causes any pending data to be
8be66a
+        flushed and any pending incoming connections to be rejected. If no, the
8be66a
+        socket's buffers won't be cleared, permitting the service to handle any
8be66a
+        pending connections after restart, which is the usually expected behaviour.
8be66a
+        Defaults to <option>no</option>.
8be66a
+        </para></listitem>
8be66a
+      </varlistentry>
8be66a
+
8be66a
       <varlistentry>
8be66a
         <term><varname>MaxConnections=</varname></term>
8be66a
         <listitem><para>The maximum number of connections to
8be66a
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
8be66a
index 913cc74918..bb77539030 100644
8be66a
--- a/src/core/dbus-socket.c
8be66a
+++ b/src/core/dbus-socket.c
8be66a
@@ -85,6 +85,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
8be66a
         SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
         SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
         SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
+        SD_BUS_PROPERTY("FlushPending", "b", bus_property_get_bool, offsetof(Socket, flush_pending), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
         SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
         SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
         SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST),
8be66a
@@ -177,6 +178,9 @@ static int bus_socket_set_transient_property(
8be66a
         if (streq(name, "Accept"))
8be66a
                 return bus_set_transient_bool(u, name, &s->accept, message, flags, error);
8be66a
 
8be66a
+        if (streq(name, "FlushPending"))
8be66a
+                return bus_set_transient_bool(u, name, &s->flush_pending, message, flags, error);
8be66a
+
8be66a
         if (streq(name, "Writable"))
8be66a
                 return bus_set_transient_bool(u, name, &s->writable, message, flags, error);
8be66a
 
8be66a
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
8be66a
index 6d21b2e433..24ee5ae6fe 100644
8be66a
--- a/src/core/load-fragment-gperf.gperf.m4
8be66a
+++ b/src/core/load-fragment-gperf.gperf.m4
8be66a
@@ -359,6 +359,7 @@ Socket.SocketGroup,              config_parse_user_group,            0,
8be66a
 Socket.SocketMode,               config_parse_mode,                  0,                             offsetof(Socket, socket_mode)
8be66a
 Socket.DirectoryMode,            config_parse_mode,                  0,                             offsetof(Socket, directory_mode)
8be66a
 Socket.Accept,                   config_parse_bool,                  0,                             offsetof(Socket, accept)
8be66a
+Socket.FlushPending,             config_parse_bool,                  0,                             offsetof(Socket, flush_pending)
8be66a
 Socket.Writable,                 config_parse_bool,                  0,                             offsetof(Socket, writable)
8be66a
 Socket.MaxConnections,           config_parse_unsigned,              0,                             offsetof(Socket, max_connections)
8be66a
 Socket.MaxConnectionsPerSource,  config_parse_unsigned,              0,                             offsetof(Socket, max_connections_per_source)
8be66a
diff --git a/src/core/socket.c b/src/core/socket.c
8be66a
index fe061eb73b..97c3a7fc9a 100644
8be66a
--- a/src/core/socket.c
8be66a
+++ b/src/core/socket.c
8be66a
@@ -70,6 +70,7 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
8be66a
 
8be66a
 static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
8be66a
 static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
8be66a
+static void flush_ports(Socket *s);
8be66a
 
8be66a
 static void socket_init(Unit *u) {
8be66a
         Socket *s = SOCKET(u);
8be66a
@@ -703,6 +704,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
8be66a
                         prefix, s->n_connections,
8be66a
                         prefix, s->max_connections,
8be66a
                         prefix, s->max_connections_per_source);
8be66a
+        else
8be66a
+                fprintf(f,
8be66a
+                        "%sFlushPending: %s\n",
8be66a
+                         prefix, yes_no(s->flush_pending));
8be66a
+
8be66a
 
8be66a
         if (s->priority >= 0)
8be66a
                 fprintf(f,
8be66a
@@ -2111,6 +2117,11 @@ static void socket_enter_listening(Socket *s) {
8be66a
         int r;
8be66a
         assert(s);
8be66a
 
8be66a
+        if (!s->accept && s->flush_pending) {
8be66a
+                log_unit_debug(UNIT(s), "Flushing socket before listening.");
8be66a
+                flush_ports(s);
8be66a
+        }
8be66a
+
8be66a
         r = socket_watch_fds(s);
8be66a
         if (r < 0) {
8be66a
                 log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
8be66a
diff --git a/src/core/socket.h b/src/core/socket.h
8be66a
index c4e25db1fc..b7a25d91fd 100644
8be66a
--- a/src/core/socket.h
8be66a
+++ b/src/core/socket.h
8be66a
@@ -109,6 +109,7 @@ struct Socket {
8be66a
         bool accept;
8be66a
         bool remove_on_stop;
8be66a
         bool writable;
8be66a
+        bool flush_pending;
8be66a
 
8be66a
         int socket_protocol;
8be66a
 
8be66a
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
8be66a
index 77788f0fe2..7029aa5615 100644
8be66a
--- a/src/shared/bus-unit-util.c
8be66a
+++ b/src/shared/bus-unit-util.c
8be66a
@@ -1468,7 +1468,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
8be66a
 
8be66a
         if (STR_IN_SET(field,
8be66a
                        "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
8be66a
-                       "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
8be66a
+                       "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet",
8be66a
+                       "FlushPending"))
8be66a
 
8be66a
                 return bus_append_parse_boolean(m, field, eq);
8be66a