]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
daemon: support backlog= listen parameter
authorEric Wong <e@80x24.org>
Tue, 8 Apr 2025 20:49:58 +0000 (20:49 +0000)
committerEric Wong <e@80x24.org>
Fri, 11 Apr 2025 08:40:39 +0000 (08:40 +0000)
For -netd instances using multiple listeners, it's easiest for me
to configure all ListenStream directives in a single
systemd.socket(5) unit.  Unfortunately, this seems to force all
listeners to use the same Backlog= value, and juggling
multiple systemd.socket(5) units for a single systemd service
seems tedious.

So support setting the backlog= parameter on the public-inbox-*d
command-line on a per-listener basis; allowing us to override
the backlog value of inherited listeners and also to set the
backlog for listeners we bind ourselves.  This makes it possible
for non-systemd users to easily configure per-listener backlogs,
as well.

Documentation/public-inbox-daemon.pod
Documentation/public-inbox-netd.pod
lib/PublicInbox/Daemon.pm
t/netd.t

index 7496cfca4ff81224f41b763e5e5c76ab175763a5..a6d93e66de5c6cf9def917ebd31c3f0f19fb0900 100644 (file)
@@ -50,7 +50,7 @@ all if relying on L<systemd.socket(5)> or similar,
 
 Per-listener options may be specified after C<?> as C<KEY=VALUE>
 pairs delimited by C<,>.  See L<public-inbox-netd(1)> for
-documentation on the C<cert=>, C<key=>, C<env.NAME=VALUE>,
+documentation on the C<backlog=>, C<cert=>, C<key=>, C<env.NAME=VALUE>,
 C<out=>, C<err=>, C<psgi=>, C<servername=>, and C<serverport=>
 options available.
 
index bbf0eb448fb1f7a5e218eb96cae7cd34db1d0fa4..d29514a2f95ac5f0c7365b30b1b5197a6598715a 100644 (file)
@@ -58,6 +58,12 @@ is printed in the the greeting and used for generating C<Xref:>
 headers, overriding the L<public-inbox-config(5)/publicinbox.nntpserver>
 directive.
 
+C<backlog=> may be set to an integer on a per-listener basis to set the
+L<listen(2)> backlog parameter in public-inbox 2.0+ on both inherited and bound
+sockets.  This parameter is a convenient way to specify per-listener backlogs
+if multiple listen sockets are defined by a single L<systemd.socket(5)> unit
+but different backlog values are desired.
+
 =item --cert /path/to/cert
 
 See L<public-inbox-daemon(1)>.
index dbac514635eb97342b65c766a236908c01755bfe..b8a51f30f593290b0c503cee50da31bb4b877b45 100644 (file)
@@ -68,6 +68,11 @@ sub listener_opt ($) {
        # p5-io-socket-ssl/example/ssl_server.pl has this fallback:
        $o->{cert} //= [ $default_cert ] if defined($default_cert);
        $o->{key} //= defined($default_key) ? [ $default_key ] : $o->{cert};
+       if ($o->{backlog}) {
+               grep /[^0-9]/, @{$o->{backlog}} and
+                       die "E: non-digit backlog in $str\n";
+               $o->{backlog} = $o->{backlog}->[-1];
+       }
        $o;
 }
 
@@ -246,7 +251,11 @@ EOF
                } elsif (defined($TLS_ONLY{$scheme})) {
                        die "$orig specified w/o cert=\n";
                }
-               if ($listener_names->{$l}) { # already inherited
+               if (my $s = $listener_names->{$l}) { # already inherited
+                       if (defined(my $bl = $opt->{backlog})) {
+                               listen($s, $bl) or
+                                       warn "W: listen($l, backlog=$bl): $!\n";
+                       }
                        $XNETD{$l} = load_mod($scheme, $opt, $l);
                        next;
                }
@@ -274,7 +283,7 @@ EOF
                        die $@ if $@;
                        %o = (LocalAddr => $l, ReuseAddr => 1, Proto => 'tcp');
                }
-               $o{Listen} = 2**31 - 1; # kernel will clamp
+               $o{Listen} = $opt->{backlog} // 2**31 - 1; # kernel will clamp
                my $prev = umask 0000;
                my $s = eval { $sock_pkg->new(%o) } or
                        warn "error binding $l: $! ($@)\n";
index abdde1241810e7e5bd8ca0ce7b95ffaca450f563..71f6f69e0cbb8afc3579143f8325ee6cd3b2b7b0 100644 (file)
--- a/t/netd.t
+++ b/t/netd.t
@@ -2,7 +2,7 @@
 # Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 use v5.12;
-use Socket qw(IPPROTO_TCP SOL_SOCKET);
+use Socket qw(IPPROTO_TCP SOL_SOCKET SOCK_STREAM);
 use PublicInbox::TestCommon;
 # IO::Poll and Net::NNTP are part of the standard library, but
 # distros may split them off...
@@ -35,7 +35,8 @@ for (1..3) {
        pipe(my ($r, $w)) or xbail "pipe: $!";
        push @pad_pipes, $r, $w;
 };
-my %srv = map { $_ => tcp_server() } qw(imap nntp imaps nntps);
+my @srv = map { $_ => tcp_server() } qw(imap nntp imaps nntps);
+my %srv = @srv;
 my $ibx = create_inbox 'netd', version => 2,
                        -primary_address => $addr, indexlevel => 'basic', sub {
        my ($im, $ibx) = @_;
@@ -55,15 +56,16 @@ $pi_config //= "$ibx->{inboxdir}/pi_config";
 my @args = ("--cert=$cert", "--key=$key");
 my $rdr = {};
 my $fd = 3;
-while (my ($k, $v) = each %srv) {
+do {
+       my ($k, $v) = splice @srv, 0, 2;
        push @args, "-l$k://".tcp_host_port($v);
+       $args[-1] .= '?servername=override.example' if $k eq 'nntp';
        $rdr->{$fd++} = $v;
-}
+} while (@srv);
 my $cmd = [ '-netd', '-W0', @args, "--stdout=$out", "--stderr=$err" ];
 my $env = { PI_CONFIG => $pi_config };
 my $td = start_script($cmd, $env, $rdr);
 @pad_pipes = ();
-undef $rdr;
 my %o = (
        SSL_hostname => 'server.local',
        SSL_verifycn_name => 'server.local',
@@ -78,7 +80,53 @@ my %o = (
 {
        my $c = tcp_connect($srv{nntp});
        my $msg = <$c>;
-       like($msg, qr/^201 .*? ready - post via email/, 'connected to NNTP');
+       like $msg, qr/^201 override\.example ready - post via email/,
+               'connected to NNTP';
+}
+
+SKIP: {
+       skip "no ss(8), not Linux: $^O", 1 if $^O ne 'linux';
+       my $ss = require_cmd 'ss', 1 or skip 'ss(8) command not found', 1;
+       $td->kill;
+       my $nr = 10;
+       my @exp; # (scheme, addr_port, expected backlog)
+       for (@$cmd) {
+               if (m!\A-l([a-z]+)://([^/\?]+)!) {
+                       push @exp, $1, $2, $nr;
+                       $_ .= /\?/ ? ',' : '?';
+                       $_ .= "backlog=$nr";
+                       $nr++;
+               }
+       }
+       my $usock = "$tmpdir/u.sock";
+       push @$cmd, "-lnntp://$usock?backlog=$nr,servername=bogus.example";
+       $td->join;
+       $td = start_script($cmd, $env, $rdr);
+       my $c = tcp_connect($srv{nntp});
+       my $msg = <$c>;
+       like $msg, qr/^201 override\.example ready - post via email/,
+               'NNTP ready after restart';
+
+       require IO::Socket::UNIX;
+       $c = IO::Socket::UNIX->new(Peer => $usock, Type => Socket::SOCK_STREAM);
+       $msg = <$c>;
+       like $msg, qr/^201 bogus\.example ready - post via email/,
+               'UNIX socket bound';
+       my @ss_after = xqx([$ss, '-nl']);
+       my @ss_u = grep /^u_str\s+LISTEN\s+\d+\s+\d+\s+\Q$usock\E\s+/, @ss_after;
+       xbail("multiple (or zero) `$usock' matches", \@ss_u) if @ss_u != 1;
+       @ss_u = split /\s+/, $ss_u[0];
+       is $ss_u[3], $nr, 'newly bound listener has expected backlog in Send-Q' or
+               diag explain(\@ss_after);
+       do {
+               my ($scheme, $addr_port, $exp_backlog) = splice @exp, 0, 3;
+               my @l = grep /^tcp\s+LISTEN\s+\d+\s+\d+\s+\Q$addr_port\E\s+/, @ss_after;
+               xbail("multiple (or zero) `$addr_port' matches", \@l) if @l != 1;
+               @l = split /\s+/, $l[0];
+               is $l[3], $exp_backlog,
+                       "inherited $scheme listener has expected backlog in Send-Q" or
+                       diag explain(\@ss_after);
+       } while (@exp);
 }
 
 # TODO: more tests