]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Daemon.pm
daemon: fix SIGUSR2 upgrade with -W0 (no workers)
[public-inbox.git] / lib / PublicInbox / Daemon.pm
index 842ff1ccfd3424b677ca66eca8cd41c740fc29fc..3d582e355ed70d40f6b9689f791f5e08f0c81d67 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2019 all contributors <meta@public-inbox.org>
+# Copyright (C) 2015-2020 all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 # 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);
@@ -403,6 +403,9 @@ sub upgrade { # $_[0] = signal name or number (unused)
                $ENV{LISTEN_FDS} = scalar @listeners;
                $ENV{LISTEN_PID} = $$;
                foreach my $s (@listeners) {
+                       # @listeners are globs with workers, PI::L w/o workers
+                       $s = $s->{sock} if ref($s) eq 'PublicInbox::Listener';
+
                        my $fl = fcntl($s, F_GETFD, 0);
                        fcntl($s, F_SETFD, $fl &= ~FD_CLOEXEC);
                }
@@ -462,23 +465,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 <<EOF;
@@ -489,10 +496,12 @@ 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 {
@@ -509,7 +518,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;
                }
@@ -560,11 +569,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
@@ -595,11 +605,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;
@@ -631,6 +640,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();