]> git.proxmox.com Git - pve-common.git/blobdiff - data/PVE/Daemon.pm
Daemon: make sure we kill all child processes on TERM
[pve-common.git] / data / PVE / Daemon.pm
index 838d59673203e3c084f28213eef719ab753022f7..b069b9923781c2b1c87e626c215a3cbbed058097 100644 (file)
@@ -20,6 +20,9 @@ use PVE::INotify;
 
 use POSIX ":sys_wait_h";
 use Fcntl ':flock';
+use Socket qw(IPPROTO_TCP TCP_NODELAY SOMAXCONN);
+use IO::Socket::INET;
+
 use Getopt::Long;
 use Time::HiRes qw (gettimeofday);
 
@@ -178,7 +181,7 @@ my $start_workers = sub {
 };
 
 my $terminate_server = sub {
-    my ($self) = @_;
+    my ($self, $allow_open_children) = @_;
 
     $self->{terminate} = 1; # set flag to avoid worker restart
 
@@ -191,24 +194,32 @@ my $terminate_server = sub {
     eval { $self->shutdown(); };
     warn $@ if $@;
 
-    # we have workers - terminate them
+    # we have workers - send TERM signal
 
     foreach my $cpid (keys %{$self->{workers}}) {
        kill(15, $cpid); # TERM childs
     }
 
-    return if $self->{got_hup_signal} &&
+    # if configured, leave children running on HUP
+    return if $allow_open_children &&
        $self->{leave_children_open_on_reload};
 
+    # else, send TERM to old workers
+    foreach my $cpid (keys %{$self->{old_workers}}) {
+       kill(15, $cpid); # TERM childs
+    }
+
     # nicely shutdown childs (give them max 10 seconds to shut down)
     my $previous_alarm = alarm(10);
     eval {
        local $SIG{ALRM} = sub { die "timeout\n" };
 
        while ((my $pid = waitpid (-1, 0)) > 0) {
-           if (defined($self->{workers}->{$pid})) {
-               delete($self->{workers}->{$pid});
-               syslog('info', "worker $pid finished");
+           foreach my $id (qw(workers old_workers)) {
+               if (defined($self->{$id}->{$pid})) {
+                   delete($self->{$id}->{$pid});
+                   syslog('info', "worker $pid finished");
+               }
            }
        }
        alarm(0); # avoid race condition
@@ -219,13 +230,15 @@ my $terminate_server = sub {
 
     if ($err) {
        syslog('err', "error stopping workers (will kill them now) - $err");
-       foreach my $cpid (keys %{$self->{workers}}) {
-           # KILL childs still alive!
-           if (kill (0, $cpid)) {
-               delete($self->{workers}->{$cpid});
-               syslog("err", "kill worker $cpid");
-               kill(9, $cpid);
-               # fixme: waitpid?
+       foreach my $id (qw(workers old_workers)) {
+           foreach my $cpid (keys %{$self->{$id}}) {
+               # KILL childs still alive!
+               if (kill (0, $cpid)) {
+                   delete($self->{$id}->{$cpid});
+                   syslog("err", "kill worker $cpid");
+                   kill(9, $cpid);
+                   # fixme: waitpid?
+               }
            }
        }
     }
@@ -280,7 +293,7 @@ my $server_run = sub {
     local $SIG{TERM} = sub {
        local ($@, $!, $?); # do not overwrite error vars
        syslog('info', "received signal TERM");
-       &$terminate_server($self);
+       &$terminate_server($self, 0);
        &$server_cleanup($self);
        &$old_sig_term(@_) if $old_sig_term;
     };
@@ -289,7 +302,7 @@ my $server_run = sub {
     local $SIG{QUIT} = sub {
        local ($@, $!, $?); # do not overwrite error vars
        syslog('info', "received signal QUIT");
-       &$terminate_server($self);
+       &$terminate_server($self, 0);
        &$server_cleanup($self);
        &$old_sig_quit(@_) if $old_sig_quit;
     };
@@ -299,7 +312,7 @@ my $server_run = sub {
        local ($@, $!, $?); # do not overwrite error vars
        syslog('info', "received signal INT");
        $SIG{INT} = 'DEFAULT'; # allow to terminate now
-       &$terminate_server($self);
+       &$terminate_server($self, 0);
        &$server_cleanup($self);
        &$old_sig_int(@_) if $old_sig_int;
     };
@@ -309,7 +322,7 @@ my $server_run = sub {
        syslog('info', "received signal HUP");
        $self->{got_hup_signal} = 1;
        if ($self->{max_workers}) {
-           &$terminate_server($self);
+           &$terminate_server($self, 1);
        } elsif ($self->can('hup')) {
            eval { $self->hup() };
            warn $@ if $@;
@@ -325,7 +338,11 @@ my $server_run = sub {
                &$old_sig_chld(@_) if $old_sig_chld;
            };
 
-           for (;;) { # forever
+           # catch worker finished during restart phase 
+           &$finish_workers($self);
+
+           # now loop forever (until we receive terminate signal)
+           for (;;) { 
                &$start_workers($self);
                sleep(5);
                &$finish_workers($self);
@@ -341,7 +358,7 @@ my $server_run = sub {
     if ($err) {
        syslog ('err', "ERROR: $err");
 
-       &$terminate_server($self);
+       &$terminate_server($self, 1);
 
        if (my $wait_time = $self->{restart_on_error}) {
            $self->restart_daemon($wait_time);
@@ -741,5 +758,49 @@ sub register_status_command {
        }});
 }
 
+# some useful helper
+
+sub create_reusable_socket {
+    my ($self, $port, $host) = @_;
+
+    die "no port specifed" if !$port;
+
+    my ($socket, $sockfd);
+
+    if (defined($sockfd = $ENV{"PVE_DAEMON_SOCKET_$port"}) &&
+       $self->{env_restart_pve_daemon}) {
+
+       die "unable to parse socket fd '$sockfd'\n" 
+           if $sockfd !~ m/^(\d+)$/;
+       $sockfd = $1; # untaint
+
+       $socket = IO::Socket::INET->new;
+       $socket->fdopen($sockfd, 'w') || 
+           die "cannot fdopen file descriptor '$sockfd' - $!\n";
+
+    } else {
+
+       $socket = IO::Socket::INET->new(
+           LocalAddr => $host,
+           LocalPort => $port,
+           Listen => SOMAXCONN,
+           Proto  => 'tcp',
+           ReuseAddr => 1) ||
+           die "unable to create socket - $@\n";
+
+       # we often observe delays when using Nagle algorithm,
+       # so we disable that to maximize performance
+       setsockopt($socket, IPPROTO_TCP, TCP_NODELAY, 1);
+
+       $ENV{"PVE_DAEMON_SOCKET_$port"} = $socket->fileno;
+    }
+
+    # remove FD_CLOEXEC bit to reuse on exec
+    $socket->fcntl(Fcntl::F_SETFD(), 0);
+    
+    return $socket;
+}
+
+
 1;