t/config_limiter.t
t/content_hash.t
t/convert-compact.t
+t/daemon.t
t/data-gen/.gitignore
t/data/0001.patch
t/data/attached-mbox-with-utf8.eml
open my $fh, '>', $f or die "open $f $!";
print $fh $str or die "print $f $!";
close $fh or die "close $f $!";
+for (qw(sys/ioctl sys/filio)) {
+ my $cfg_name = $_;
+ my $cpp_name = uc $_;
+ $cfg_name =~ tr!/!!d;
+ $cpp_name =~ tr!/!_!;
+ ($Config{"i_$cfg_name"} // '') eq 'define' and
+ push @cflags, "-DHAVE_${cpp_name}_H";
+}
system($cc, '-o', $x, $f, @cflags) == 0 or die "$cc failed \$?=$?";
print STDERR '# %Config',
(map { " $_=$Config{$_}" } qw(ptrsize sizesize lseeksize)), "\n";
#include <stddef.h>
#include <sys/socket.h>
#include <sys/syscall.h>
-#include <sys/ioctl.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
#ifdef __linux__
# include <linux/fs.h>
# include <sys/epoll.h>
#endif /* Linux, any other OSes with stable syscalls? */
D(SIGWINCH);
+ X(FIONREAD);
MAYBE D(SO_ACCEPTFILTER);
MAYBE D(_SC_NPROCESSORS_ONLN);
MAYBE D(_SC_AVPHYS_PAGES);
use IO::Handle; # ->autoflush
use IO::Socket;
use File::Spec;
+use IO::Poll qw(POLLIN POLLHUP);
use POSIX qw(WNOHANG :signal_h F_SETFD);
use Socket qw(IPPROTO_TCP SOL_SOCKET);
STDOUT->autoflush(1);
do_chown($path);
}
+sub stream_hup ($) {
+ my $ev = POLLIN | POLLHUP;
+ my $n = IO::Poll::_poll(0, fileno($_[0]) // return, $ev) or return;
+ return 1 if $ev & POLLHUP;
+
+ # n.b. POLLHUP isn't reliably detected, so check FIONREAD on POLLIN
+ if (defined(PublicInbox::Syscall::FIONREAD) && ($ev & POLLIN)) {
+ ioctl($_[0], PublicInbox::Syscall::FIONREAD, $n = "") //
+ return;
+ return 1 if unpack('i', $n) == 0;
+ }
+ undef;
+}
+
+if (PublicInbox::Syscall->can('TCP_ESTABLISHED')) {
+ eval <<EOM;
+sub tcp_hup (\$) {
+ my \$buf = getsockopt(\$_[0], Socket::IPPROTO_TCP, Socket::TCP_INFO)
+ or return;
+ unpack('C', \$buf) != PublicInbox::Syscall::TCP_ESTABLISHED
+}
+EOM
+ warn "E: $@" if $@;
+}
+
+no warnings 'once';
+*tcp_hup = \&stream_hup if !__PACKAGE__->can('tcp_hup');
+
1;
'pi-httpd.async' => 1,
'pi-httpd.app' => $self->{app},
'pi-httpd.warn_cb' => $self->{warn_cb},
+ 'pi-httpd.ckhup' => $port ? \&PublicInbox::Daemon::tcp_hup :
+ \&PublicInbox::Daemon::stream_hup,
}
}
if ($^O eq 'linux') {
%CONST = (
MSG_MORE => 0x8000,
+ FIONREAD => 0x541b,
+ TCP_ESTABLISHED => 1,
TMPL_cmsg_len => TMPL_size_t,
# cmsg_len, cmsg_level, cmsg_type
SIZEOF_cmsghdr => SIZEOF_int * 2 + SIZEOF_size_t,
} elsif ($^O =~ /\A(?:freebsd|openbsd|netbsd|dragonfly)\z/) {
%CONST = (
TMPL_cmsg_len => 'L', # socklen_t
+ FIONREAD => 0x4004667f,
SIZEOF_cmsghdr => SIZEOF_int * 3,
CMSG_DATA_off => SIZEOF_ptr == 8 ? '@16' : '',
TMPL_msghdr => 'PL' . # msg_name, msg_namelen
TMPL_size_t. # msg_controllen
'i', # msg_flags
- )
+ );
+ # *BSD uses `TCPS_ESTABLISHED', not `TCP_ESTABLISHED'
+ # dragonfly uses TCPS_ESTABLISHED==5, but it lacks TCP_INFO,
+ # so leave it unset on dfly
+ $CONST{TCP_ESTABLISHED} = 4 if $^O ne 'dragonfly';
}
$CONST{CMSG_ALIGN_size} = SIZEOF_size_t;
$CONST{SIZEOF_cmsghdr} //= 0;
$CONST{CMSG_DATA_off} //= undef;
$CONST{TMPL_msghdr} //= undef;
$CONST{MSG_MORE} //= 0;
+ $CONST{FIONREAD} //= undef;
}
# SFD_CLOEXEC is arch-dependent, so IN_CLOEXEC may be, too
--- /dev/null
+# 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 autodie;
+use PublicInbox::TestCommon;
+use Socket qw(SOCK_STREAM);
+require_ok 'PublicInbox::Daemon';
+use PublicInbox::IO qw(poll_in);
+
+# $fn is stream_hup or tcp_hup
+my $ck_hup = sub {
+ my ($s, $connect, $fn, $type) = @_;
+ my $c = $connect->();
+ poll_in $s; # needed on *BSD
+ my $addr = accept(my $acc, $s);
+ close $c;
+ my $ck = PublicInbox::Daemon->can($fn);
+ poll_in($acc) if $^O ne 'linux'; # netbsd needs this, at least...
+ ok $ck->($acc), "$fn detected close ($type)";
+ $c = $connect->();
+ syswrite $c, 'hi';
+ poll_in $s; # needed on *BSD
+ $addr = accept($acc, $s);
+ ok !$ck->($acc), "$fn false when still established ($type)";
+};
+
+if (1) {
+ my $tmpdir = tmpdir;
+ my $l = "$tmpdir/named.sock";
+ my $s = IO::Socket::UNIX->new(Listen => 5, Local => $l,
+ Type => SOCK_STREAM)
+ or xbail "bind+listen($l): $!";
+ my $connect = sub {
+ IO::Socket::UNIX->new(Peer => $l, Type => SOCK_STREAM) or
+ xbail "connect($l): $!";
+ };
+ $ck_hup->($s, $connect, 'stream_hup', 'UNIX');
+}
+
+{
+ my $s = tcp_server;
+ my $tcp_conn = sub { tcp_connect($s) };
+ $ck_hup->($s, $tcp_conn, 'stream_hup', 'TCP');
+ $ck_hup->($s, $tcp_conn, 'tcp_hup', 'TCP');
+}
+
+SKIP: {
+ $^O =~ /\A(?:linux|freebsd|netbsd)\z/ or
+ skip "no TCP_INFO support $^O", 1;
+ isnt \&PublicInbox::Daemon::stream_hup,
+ \&PublicInbox::Daemon::tcp_hup,
+ "stream_hup and tcp_hup are different on \$^O=$^O";
+}
+
+done_testing;