X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FDaemon.pm;h=292bc339a73c086f06c4a34bc47f63e3e962b4ed;hb=55b707d788ce13696e4411389583e720ea6dab01;hp=c2c05b96c8ad85ab8136ba3e6580e79465df915d;hpb=0541761b0558317be1825710c1545f6e718103f2;p=public-inbox.git diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index c2c05b96..292bc339 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -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,13 +25,10 @@ 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 $cleanup; my ($uid, $gid); my ($default_cert, $default_key); -END { $cleanup->() if $cleanup }; my %KNOWN_TLS = ( 443 => 'https', 563 => 'nntps' ); my %KNOWN_STARTTLS = ( 119 => 'nntp' ); @@ -79,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: $!"; @@ -101,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; @@ -130,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'; @@ -161,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; } @@ -245,14 +243,11 @@ sub daemonize () { die "could not fork: $!\n" unless defined $pid; exit if $pid; } - if (defined $pid_file) { - write_pid($pid_file); - my $unlink_pid = $$; - $cleanup = sub { - $cleanup = undef; # avoid cyclic reference - unlink_pid_file_safe_ish($unlink_pid, $pid_file); - }; - } + return unless defined $pid_file; + + write_pid($pid_file); + # for ->DESTROY: + bless { pid => $$, pid_file => $pid_file }, __PACKAGE__; } sub worker_quit { # $_[0] = signal name or number (unused) @@ -358,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 @@ -374,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)"; @@ -466,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 { @@ -513,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; } @@ -631,16 +633,16 @@ sub daemon_loop ($$$$) { PublicInbox::DS->SetLoopTimeout(1000); } PublicInbox::DS->EventLoop; - $parent_pipe = undef; } - sub run ($$$;$) { my ($default, $refresh, $post_accept, $nntpd) = @_; daemon_prepare($default); my $af_default = $default =~ /:8080\z/ ? 'httpready' : undef; - daemonize(); + my $for_destroy = daemonize(); daemon_loop($refresh, $post_accept, $nntpd, $af_default); + PublicInbox::DS->Reset; + # ->DESTROY runs when $for_destroy goes out-of-scope } sub do_chown ($) { @@ -656,4 +658,8 @@ sub write_pid ($) { do_chown($path); } +sub DESTROY { + unlink_pid_file_safe_ish($_[0]->{pid}, $_[0]->{pid_file}); +} + 1;