X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FDaemon.pm;h=b3743f5c3ddf80d608c7becf4bc48d242f04eb88;hb=9bd675d33ad1e49bd2ebe12a1d216216e61380de;hp=c4481555d43e66a7065b21e39bce4648a5780c85;hpb=87a03babc14247d4eac489beb95abba47cf4f358;p=public-inbox.git diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index c4481555..b3743f5c 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2018 all contributors +# Copyright (C) 2015-2019 all contributors # License: AGPL-3.0+ # contains common daemon code for the nntpd and httpd servers. # This may be used for read-only IMAP server if we decide to implement it. @@ -8,12 +8,14 @@ use warnings; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IO::Handle; use IO::Socket; +use POSIX qw(WNOHANG); +use Socket qw(IPPROTO_TCP SOL_SOCKET); +sub SO_ACCEPTFILTER () { 0x1000 } use Cwd qw/abs_path/; STDOUT->autoflush(1); STDERR->autoflush(1); use PublicInbox::DS qw(now); require PublicInbox::EvCleanup; -require POSIX; require PublicInbox::Listener; require PublicInbox::ParentPipe; my @CMD; @@ -153,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; } } @@ -250,13 +252,11 @@ sub daemonize () { sub worker_quit { - my ($reason) = @_; # killing again terminates immediately: exit unless @listeners; $_->close foreach @listeners; # call PublicInbox::DS::close @listeners = (); - $reason->close if ref($reason) eq 'PublicInbox::ParentPipe'; my $proc_name; my $warn = 0; @@ -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 { @@ -429,7 +437,7 @@ sub upgrade_aborted ($) { sub reap_children () { while (1) { - my $p = waitpid(-1, &POSIX::WNOHANG) or return; + my $p = waitpid(-1, WNOHANG) or return; if (defined $reexec_pid && $p == $reexec_pid) { upgrade_aborted($p); } elsif (defined(my $id = delete $pids{$p})) { @@ -552,8 +560,25 @@ sub tls_start_cb ($$) { } } -sub daemon_loop ($$$) { - my ($refresh, $post_accept, $nntpd) = @_; +sub defer_accept ($$) { + my ($s, $af_name) = @_; + return unless defined $af_name; + if ($^O eq 'linux') { + my $x = getsockopt($s, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT()); + return unless defined $x; # may be Unix socket + my $sec = unpack('i', $x); + return if $sec > 0; # systemd users may set a higher value + setsockopt($s, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT(), 1); + } elsif ($^O eq 'freebsd') { + my $x = getsockopt($s, SOL_SOCKET, SO_ACCEPTFILTER); + return if defined $x; # don't change if set + my $accf_arg = pack('a16a240', $af_name, ''); + setsockopt($s, SOL_SOCKET, SO_ACCEPTFILTER, $accf_arg); + } +} + +sub daemon_loop ($$$$) { + my ($refresh, $post_accept, $nntpd, $af_default) = @_; PublicInbox::EvCleanup::enable(); # early for $refresh my %post_accept; while (my ($k, $v) = each %tls_opt) { @@ -571,7 +596,7 @@ sub daemon_loop ($$$) { } else { reopen_logs(); $set_user->() if $set_user; - $SIG{USR2} = sub { worker_quit('USR2') if upgrade() }; + $SIG{USR2} = sub { worker_quit() if upgrade() }; $refresh->(); } $uid = $gid = undef; @@ -581,10 +606,15 @@ sub daemon_loop ($$$) { $SIG{HUP} = $refresh; $SIG{CHLD} = 'DEFAULT'; $SIG{$_} = 'IGNORE' for qw(USR2 TTIN TTOU WINCH); - # this calls epoll_create: - @listeners = map { - PublicInbox::Listener->new($_, - $post_accept{sockname($_)} || $post_accept) + @listeners = map {; + my $tls_cb = $post_accept{sockname($_)}; + + # NNTPS, HTTPS, HTTP, and POP3S are client-first traffic + # NNTP and POP3 are server-first + defer_accept($_, $tls_cb ? 'dataready' : $af_default); + + # this calls epoll_create: + PublicInbox::Listener->new($_, $tls_cb || $post_accept) } @listeners; PublicInbox::DS->EventLoop; $parent_pipe = undef; @@ -594,8 +624,9 @@ sub daemon_loop ($$$) { sub run ($$$;$) { my ($default, $refresh, $post_accept, $nntpd) = @_; daemon_prepare($default); + my $af_default = $default =~ /:8080\z/ ? 'httpready' : undef; daemonize(); - daemon_loop($refresh, $post_accept, $nntpd); + daemon_loop($refresh, $post_accept, $nntpd, $af_default); } sub do_chown ($) {