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);
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);
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: $!";
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;
}
# 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';
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;
}
$@ ? ('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
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)";
}
}
+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 <<EOF;
}
},
HUP => sub {
+ return unless @listeners;
$worker_processes = $set_workers;
kill_workers($_[0]);
},
TTIN => sub {
+ return unless @listeners;
if ($set_workers > $worker_processes) {
++$worker_processes;
} else {
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;
}
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();