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