]> Sergey Matveev's repositories - public-inbox.git/commitdiff
daemon: warn on inheriting blocking listeners
authorEric Wong <e@80x24.org>
Sun, 30 Jun 2019 22:19:39 +0000 (22:19 +0000)
committerEric Wong <e@80x24.org>
Sun, 30 Jun 2019 22:25:25 +0000 (22:25 +0000)
For users relying on socket activation via service manager (e.g.
systemd) and running multiple service instances (@1, @2),
we need to ensure configuration of the socket is NonBlocking.
Otherwise, service managers such as systemd may clear the
O_NONBLOCK flag for a small window where accept/accept4
blocks:

public-inbox-nntpd@1      |systemd         |public-inbox-nntpd@2
--------------------------+----------------+--------------------
F_SETFL,O_NONBLOCK|O_RDWR |                | (not running, yet)
                          |F_SETFL, O_RDWR |
                          |fork+exec @2... |
accept(...) # blocks!     |                |(started by systemd)
                          |                |F_SETFL,O_NONBLOCK|O_RDWR
                          |                |accept(...) non-blocking

It's a very small window where O_NONBLOCK can be cleared,
but it exists, and I finally hit it after many years.

lib/PublicInbox/Daemon.pm
lib/PublicInbox/Listener.pm
t/common.perl

index 2b7ac266dfabbccfa83c106c0a114d2f1a446faf..2046a7f5abffd382d8a79f2e7aa2c5ee1d923432 100644 (file)
@@ -155,9 +155,9 @@ sub daemon_prepare ($) {
                my $s = eval { $sock_pkg->new(%o) };
                warn "error binding $l: $! ($@)\n" unless $s;
                umask $prev;
-
                if ($s) {
                        $listener_names{sockname($s)} = $s;
+                       $s->blocking(0);
                        push @listeners, $s;
                }
        }
@@ -363,6 +363,14 @@ sub inherit () {
        foreach my $fd (3..$end) {
                my $s = IO::Handle->new_from_fd($fd, 'r');
                if (my $k = sockname($s)) {
+                       if ($s->blocking) {
+                               $s->blocking(0);
+                               warn <<"";
+Inherited socket (fd=$fd) is blocking, making it non-blocking.
+Set 'NonBlocking = true' in the systemd.service unit to avoid stalled
+processes when multiple service instances start.
+
+                       }
                        $listener_names{$k} = $s;
                        push @rv, $s;
                } else {
index 594dabb83e38eacd2b2733c952c205e3ffdc2456..604289343260a54f22790795c20ead9b7d8c85da 100644 (file)
@@ -16,7 +16,6 @@ sub new ($$$) {
        setsockopt($s, SOL_SOCKET, SO_KEEPALIVE, 1);
        setsockopt($s, IPPROTO_TCP, TCP_NODELAY, 1); # ignore errors on non-TCP
        listen($s, 1024);
-       IO::Handle::blocking($s, 0);
        my $self = fields::new($class);
        $self->SUPER::new($s, EPOLLIN|EPOLLET|EPOLLEXCLUSIVE);
        $self->{post_accept} = $cb;
index 3f05b68a368403d7acecac6b9b83ab0b7a87aa12..91d65c5fa380a41b47420993198a57dfada4bc6d 100644 (file)
@@ -24,15 +24,18 @@ sub tcp_server () {
                Proto => 'tcp',
                Type => Socket::SOCK_STREAM(),
                Listen => 1024,
+               Blocking => 0,
        )
 }
 
 sub unix_server ($) {
-       IO::Socket::UNIX->new(
+       my $s = IO::Socket::UNIX->new(
                Listen => 1024,
                Type => Socket::SOCK_STREAM(),
                Local => $_[0],
-       )
+       );
+       $s->blocking(0);
+       $s;
 }
 
 sub spawn_listener {