X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FDaemon.pm;h=227ba5f979d09ef23eebaf892284b27137222165;hb=b6f480ed58abc5ae2a426ef4f792621b9d3cf283;hp=b76b9ffbf91ac944518aa72d43ed43bcde618174;hpb=b6e2d71cd342a55e942915a082ca3fd15812e48d;p=public-inbox.git diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index b76b9ffb..227ba5f9 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -1,5 +1,5 @@ -# Copyright (C) 2015 all contributors -# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt) +# Copyright (C) 2015-2018 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. package PublicInbox::Daemon; @@ -9,9 +9,11 @@ use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IO::Handle; use IO::Socket; use Cwd qw/abs_path/; +use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC); STDOUT->autoflush(1); STDERR->autoflush(1); -require Danga::Socket; +require PublicInbox::DS; +require PublicInbox::EvCleanup; require POSIX; require PublicInbox::Listener; require PublicInbox::ParentPipe; @@ -102,17 +104,18 @@ sub check_absolute ($$) { } sub daemonize () { - foreach my $i (0..$#ARGV) { - my $arg = $ARGV[$i]; - next unless -e $arg; - $ARGV[$i] = abs_path($arg); - } - check_absolute('stdout', $stdout); - check_absolute('stderr', $stderr); - check_absolute('pid-file', $pid_file); + if ($daemonize) { + foreach my $i (0..$#ARGV) { + my $arg = $ARGV[$i]; + next unless -e $arg; + $ARGV[$i] = abs_path($arg); + } + check_absolute('stdout', $stdout); + check_absolute('stderr', $stderr); + check_absolute('pid-file', $pid_file); - chdir '/' or die "chdir failed: $!"; - open(STDIN, '+<', '/dev/null') or die "redirect stdin failed: $!"; + chdir '/' or die "chdir failed: $!"; + } return unless (defined $pid_file || defined $group || defined $user || $daemonize); @@ -145,6 +148,8 @@ sub daemonize () { die "could not fork: $!\n" unless defined $pid; exit if $pid; + open(STDIN, '+<', '/dev/null') or + die "redirect stdin failed: $!\n"; open STDOUT, '>&STDIN' or die "redirect stdout failed: $!\n"; open STDERR, '>&STDIN' or die "redirect stderr failed: $!\n"; POSIX::setsid(); @@ -168,19 +173,21 @@ sub worker_quit { # killing again terminates immediately: exit unless @listeners; - $_->close foreach @listeners; # call Danga::Socket::close + $_->close foreach @listeners; # call PublicInbox::DS::close @listeners = (); $reason->close if ref($reason) eq 'PublicInbox::ParentPipe'; my $proc_name; my $warn = 0; # drop idle connections and try to quit gracefully - Danga::Socket->SetPostLoopCallback(sub { + PublicInbox::DS->SetPostLoopCallback(sub { my ($dmap, undef) = @_; my $n = 0; + my $now = clock_gettime(CLOCK_MONOTONIC); foreach my $s (values %$dmap) { - if ($s->can('busy') && $s->busy) { + $s->can('busy') or next; + if ($s->busy($now)) { ++$n; } else { # close as much as possible, early as possible @@ -228,6 +235,28 @@ sub sockname ($) { "$host:$port"; } +sub unpack_ipv6 ($) { + my ($addr) = @_; + my ($port, $host); + + # Socket.pm in Perl 5.14+ supports IPv6: + eval { + ($port, $host) = Socket::unpack_sockaddr_in6($addr); + $host = Socket::inet_ntop(Socket::AF_INET6(), $host); + }; + + if ($@) { + # Perl 5.12 or earlier? SpamAssassin and Net::Server use + # Socket6, so it may be installed on our system, already + # (otherwise die here): + require Socket6; + + ($port, $host) = Socket6::unpack_sockaddr_in6($addr); + $host = Socket6::inet_ntop(Socket6::AF_INET6(), $host); + } + ($host, $port); +} + sub host_with_port ($) { my ($addr) = @_; my ($port, $host); @@ -235,9 +264,7 @@ sub host_with_port ($) { # this eval will die on Unix sockets: eval { if (length($addr) >= 28) { - require Socket6; - ($port, $host) = Socket6::unpack_sockaddr_in6($addr); - $host = Socket6::inet_ntop(Socket6::AF_INET6(), $host); + ($host, $port) = unpack_ipv6($addr); $host = "[$host]"; } else { ($port, $host) = Socket::sockaddr_in($addr); @@ -350,6 +377,11 @@ sub unlink_pid_file_safe_ish ($$) { sub master_loop { pipe(my ($p0, $p1)) or die "failed to create parent-pipe: $!"; pipe(my ($r, $w)) or die "failed to create self-pipe: $!"; + + if ($^O eq 'linux') { # 1031: F_SETPIPE_SZ = 1031 + fcntl($_, 1031, 4096) for ($w, $p1); + } + IO::Handle::blocking($w, 0); my $set_workers = $worker_processes; my @caught; @@ -377,7 +409,7 @@ sub master_loop { } elsif ($s eq 'WINCH') { if (-t STDIN || -t STDOUT || -t STDERR) { warn -"ignoring SIGWINCH while connected to terminal\n"; +"ignoring SIGWINCH since we are not daemonized\n"; $SIG{WINCH} = 'IGNORE'; } else { $worker_processes = 0; @@ -432,6 +464,7 @@ sub master_loop { sub daemon_loop ($$) { my ($refresh, $post_accept) = @_; + PublicInbox::EvCleanup::enable(); # early for $refresh my $parent_pipe; if ($worker_processes > 0) { $refresh->(); # preload by default @@ -448,12 +481,13 @@ sub daemon_loop ($$) { $SIG{QUIT} = $SIG{INT} = $SIG{TERM} = *worker_quit; $SIG{USR1} = *reopen_logs; $SIG{HUP} = $refresh; - $SIG{$_} = 'DEFAULT' for qw(CHLD USR2 TTIN TTOU WINCH); + $SIG{CHLD} = 'DEFAULT'; + $SIG{$_} = 'IGNORE' for qw(USR2 TTIN TTOU WINCH); # this calls epoll_create: @listeners = map { PublicInbox::Listener->new($_, $post_accept) } @listeners; - Danga::Socket->EventLoop; + PublicInbox::DS->EventLoop; $parent_pipe = undef; }