diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 0037a37f2a..cc6dfa8c1b 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -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; diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 0f38f9a0f3..c634f1d564 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -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); + } +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index d17a2f35f8..9f6a301368 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -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))) diff --git a/src/core/socket.c b/src/core/socket.c index 50912c33dd..091567ed4f 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -28,7 +28,6 @@ #include #include -#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; }