X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FDaemon.pm;h=43ef2691341a7cd46924541a103c27b8b1e5c79b;hb=98407632803aa6441e7882c713d55a399a25b710;hp=dd9e7780d695b9ae499da96fdc229221b7482482;hpb=b93c7a7efed33c68a2cf229d3086d4edec082149;p=public-inbox.git
diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm
index dd9e7780..43ef2691 100644
--- a/lib/PublicInbox/Daemon.pm
+++ b/lib/PublicInbox/Daemon.pm
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2019 all contributors
+# Copyright (C) 2015-2020 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.
@@ -6,7 +6,7 @@ package PublicInbox::Daemon;
use strict;
use warnings;
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
-use IO::Handle;
+use IO::Handle; # ->autoflush
use IO::Socket;
use POSIX qw(WNOHANG :signal_h);
use Socket qw(IPPROTO_TCP SOL_SOCKET);
@@ -25,7 +25,6 @@ my (@cfg_listen, $stdout, $stderr, $group, $user, $pid_file, $daemonize);
my $worker_processes = 1;
my @listeners;
my %pids;
-my %listener_names; # sockname => IO::Handle
my %tls_opt; # scheme://sockname => args for IO::Socket::SSL->start_SSL
my $reexec_pid;
my ($uid, $gid);
@@ -77,6 +76,7 @@ sub sig_setmask { sigprocmask(SIG_SETMASK, @_) or die "sigprocmask: $!" }
sub daemon_prepare ($) {
my ($default_listen) = @_;
+ my $listener_names = {}; # sockname => IO::Handle
$oldset = POSIX::SigSet->new();
$newset = POSIX::SigSet->new();
$newset->fillset or die "fillset: $!";
@@ -99,11 +99,11 @@ sub daemon_prepare ($) {
if (defined $pid_file && $pid_file =~ /\.oldbin\z/) {
die "--pid-file cannot end with '.oldbin'\n";
}
- @listeners = inherit();
+ @listeners = inherit($listener_names);
# allow socket-activation users to set certs once and not
# have to configure each socket:
- my @inherited_names = keys(%listener_names) if defined($default_cert);
+ my @inherited_names = keys(%$listener_names) if defined($default_cert);
# ignore daemonize when inheriting
$daemonize = undef if scalar @listeners;
@@ -128,7 +128,7 @@ sub daemon_prepare ($) {
}
# TODO: use scheme to load either NNTP.pm or HTTP.pm
- next if $listener_names{$l}; # already inherited
+ next if $listener_names->{$l}; # already inherited
my (%o, $sock_pkg);
if (index($l, '/') == 0) {
$sock_pkg = 'IO::Socket::UNIX';
@@ -159,7 +159,7 @@ sub daemon_prepare ($) {
warn "error binding $l: $! ($@)\n" unless $s;
umask $prev;
if ($s) {
- $listener_names{sockname($s)} = $s;
+ $listener_names->{sockname($s)} = $s;
$s->blocking(0);
push @listeners, $s;
}
@@ -353,7 +353,8 @@ sub host_with_port ($) {
$@ ? ('127.0.0.1', 0) : ($host, $port);
}
-sub inherit () {
+sub inherit ($) {
+ my ($listener_names) = @_;
return () if ($ENV{LISTEN_PID} || 0) != $$;
my $fds = $ENV{LISTEN_FDS} or return ();
my $end = $fds + 2; # LISTEN_FDS_START - 1
@@ -369,7 +370,7 @@ Set 'NonBlocking = true' in the systemd.service unit to avoid stalled
processes when multiple service instances start.
}
- $listener_names{$k} = $s;
+ $listener_names->{$k} = $s;
push @rv, $s;
} else {
warn "failed to inherit fd=$fd (LISTEN_FDS=$fds)";
@@ -461,23 +462,27 @@ sub unlink_pid_file_safe_ish ($$) {
}
}
+sub master_quit ($) {
+ exit unless @listeners;
+ @listeners = ();
+ kill_workers($_[0]);
+}
+
sub master_loop {
pipe(my ($p0, $p1)) or die "failed to create parent-pipe: $!";
# 1031: F_SETPIPE_SZ, 4096: page size
fcntl($p1, 1031, 4096) if $^O eq 'linux';
my $set_workers = $worker_processes;
reopen_logs();
- my $quit = 0;
my $ignore_winch;
- my $quit_cb = sub { exit if $quit++; kill_workers($_[0]) };
my $sig = {
USR1 => sub { reopen_logs(); kill_workers($_[0]); },
USR2 => \&upgrade,
- QUIT => $quit_cb,
- INT => $quit_cb,
- TERM => $quit_cb,
+ QUIT => \&master_quit,
+ INT => \&master_quit,
+ TERM => \&master_quit,
WINCH => sub {
- return if $ignore_winch;
+ return if $ignore_winch || !@listeners;
if (-t STDIN || -t STDOUT || -t STDERR) {
$ignore_winch = 1;
warn < sub {
+ return unless @listeners;
$worker_processes = $set_workers;
kill_workers($_[0]);
},
TTIN => sub {
+ return unless @listeners;
if ($set_workers > $worker_processes) {
++$worker_processes;
} else {
@@ -508,7 +515,7 @@ EOF
sig_setmask($oldset) if !$sigfd;
while (1) { # main loop
my $n = scalar keys %pids;
- if ($quit) {
+ unless (@listeners) {
exit if $n == 0;
$set_workers = $worker_processes = $n = 0;
}
@@ -559,11 +566,12 @@ 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());
+ my $TCP_DEFER_ACCEPT = 9; # Socket::TCP_DEFER_ACCEPT is in 5.14+
+ my $x = getsockopt($s, IPPROTO_TCP, $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);
+ setsockopt($s, IPPROTO_TCP, $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
@@ -594,11 +602,10 @@ sub daemon_loop ($$$$) {
WINCH => 'IGNORE',
CHLD => \&PublicInbox::DS::enqueue_reap,
};
- my $parent_pipe;
if ($worker_processes > 0) {
$refresh->(); # preload by default
my $fh = master_loop(); # returns if in child process
- $parent_pipe = PublicInbox::ParentPipe->new($fh, *worker_quit);
+ PublicInbox::ParentPipe->new($fh, \&worker_quit);
} else {
reopen_logs();
$set_user->() if $set_user;
@@ -630,6 +637,7 @@ sub daemon_loop ($$$$) {
sub run ($$$;$) {
my ($default, $refresh, $post_accept, $nntpd) = @_;
+ local $SIG{PIPE} = 'IGNORE';
daemon_prepare($default);
my $af_default = $default =~ /:8080\z/ ? 'httpready' : undef;
my $for_destroy = daemonize();