]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: rework how we flush incoming traffic when a socket unit goes down 3202/head
authorLennart Poettering <lennart@poettering.net>
Fri, 6 May 2016 11:29:26 +0000 (13:29 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 6 May 2016 11:29:26 +0000 (13:29 +0200)
Previously, we'd simply close and reopen the socket file descriptors. This is
problematic however, as we won't transition through the SOCKET_CHOWN state
then, and thus the file ownership won't be correct for the sockets.

Rework the flushing logic, and actually read any queued data from the sockets
for flushing, and accept any queued messages and disconnect them.

src/basic/io-util.c
src/basic/socket-util.c
src/basic/socket-util.h
src/core/socket.c

index 0037a37f2a458151bb62d1ec77202ac64bb76c92..cc6dfa8c1b94a29bcaf36cdb139be8dc09467178 100644 (file)
@@ -33,6 +33,11 @@ int flush_fd(int fd) {
                 .events = POLLIN,
         };
 
+        /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
+         * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
+         * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used
+         * was set to non-blocking too. */
+
         for (;;) {
                 char buf[LINE_MAX];
                 ssize_t l;
index 0f38f9a0f3a563264953fbb19e9efa3407bb8b1b..c634f1d5649d71ef7c362bdc9870a5f9fde8bab3 100644 (file)
@@ -23,6 +23,7 @@
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/ip.h>
+#include <poll.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -970,3 +971,42 @@ fallback:
 
         return (ssize_t) k;
 }
+
+int flush_accept(int fd) {
+
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = POLLIN,
+        };
+        int r;
+
+
+        /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */
+
+        for (;;) {
+                int cfd;
+
+                r = poll(&pollfd, 1, 0);
+                if (r < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+
+                } else if (r == 0)
+                        return 0;
+
+                cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+                if (cfd < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        if (errno == EAGAIN)
+                                return 0;
+
+                        return -errno;
+                }
+
+                close(cfd);
+        }
+}
index d17a2f35f822bcbdfbf9baf0fdd3e29bb55de8e6..9f6a30136888452d90c01b03f1134fec752c2a47 100644 (file)
@@ -135,5 +135,7 @@ int receive_one_fd(int transport_fd, int flags);
 
 ssize_t next_datagram_size_fd(int fd);
 
+int flush_accept(int fd);
+
 #define CMSG_FOREACH(cmsg, mh)                                          \
         for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
index 50912c33dd9fc8c43a34a356d03b168034aa7bd6..091567ed4f3987080558f913f30b90a5706732f3 100644 (file)
@@ -28,7 +28,6 @@
 #include <unistd.h>
 #include <linux/sctp.h>
 
-#include "sd-event.h"
 #include "alloc-util.h"
 #include "bus-error.h"
 #include "bus-util.h"
@@ -38,6 +37,7 @@
 #include "exit-status.h"
 #include "fd-util.h"
 #include "formats-util.h"
+#include "io-util.h"
 #include "label.h"
 #include "log.h"
 #include "missing.h"
@@ -1960,6 +1960,21 @@ fail:
         socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
 }
 
+static void flush_ports(Socket *s) {
+        SocketPort *p;
+
+        /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy
+         * anymore */
+
+        LIST_FOREACH(port, p, s->ports) {
+                if (p->fd < 0)
+                        continue;
+
+                (void) flush_accept(p->fd);
+                (void) flush_fd(p->fd);
+        }
+}
+
 static void socket_enter_running(Socket *s, int cfd) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
@@ -1969,31 +1984,15 @@ static void socket_enter_running(Socket *s, int cfd) {
 
         assert(s);
 
-        /* We don't take connections anymore if we are supposed to
-         * shut down anyway */
+        /* We don't take connections anymore if we are supposed to shut down anyway */
         if (unit_stop_pending(UNIT(s))) {
 
                 log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
 
                 if (cfd >= 0)
                         cfd = safe_close(cfd);
-                else  {
-                        /* Flush all sockets by closing and reopening them */
-                        socket_close_fds(s);
-
-                        r = socket_open_fds(s);
-                        if (r < 0) {
-                                log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
-                                socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-                                return;
-                        }
-
-                        r = socket_watch_fds(s);
-                        if (r < 0) {
-                                log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
-                                socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-                        }
-                }
+                else
+                        flush_ports(s);
 
                 return;
         }