# Abstract class to implement Daemons
#
# Features:
-# * lock and write PID file /var/run/$name.pid to make sure onyl
+# * lock and write PID file /var/run/$name.pid to make sure only
# one instance is running.
# * keep lock open during restart
# * correctly daemonize (redirect STDIN/STDOUT)
# * allow to restart while workers are still runningl
# (option 'leave_children_open_on_reload')
# * run as different user using setuid/setgid
-
+
use strict;
use warnings;
use English;
for my $sig (qw(CHLD HUP INT TERM QUIT)) {
$SIG{$sig} = 'DEFAULT'; # restore default handler
- # AnyEvent signals only works if $SIG{XX} is
+ # AnyEvent signals only works if $SIG{XX} is
# undefined (perl event loop)
delete $SIG{$sig}; # so that we can handle events with AnyEvent
}
if (my $fd = $self->{env_pve_lock_fd}) {
$self->{daemon_lock_fh} = IO::Handle->new_from_fd($fd, "a");
-
+
} else {
$waittime = 5;
my ($running, $pid) = $self->running();
if ($running) {
- die "can't aquire lock '$lkfn' - daemon already started (pid = $pid)\n";
+ die "can't acquire lock '$lkfn' - daemon already started (pid = $pid)\n";
} else {
- die "can't aquire lock '$lkfn' - $err\n";
+ die "can't acquire lock '$lkfn' - $err\n";
}
}
};
return if $self->{terminate};
- my $count = 0;
- foreach my $cpid (keys %{$self->{workers}}) {
- $count++;
- }
-
+ my $count = scalar keys %{$self->{workers}};
my $need = $self->{max_workers} - $count;
return if $need <= 0;
}
};
+my $terminate_old_workers = sub {
+ my ($self) = @_;
+
+ # if list is empty kill sends no signal, so no checks needed
+ kill 15, keys %{$self->{old_workers}};
+};
+
my $terminate_server = sub {
my ($self, $allow_open_children) = @_;
$self->{terminate} = 1; # set flag to avoid worker restart
- if (!$self->{max_workers}) {
- eval { $self->shutdown(); };
- warn $@ if $@;
- return;
- }
-
eval { $self->shutdown(); };
warn $@ if $@;
- # we have workers - send TERM signal
-
- foreach my $cpid (keys %{$self->{workers}}) {
- kill(15, $cpid); # TERM childs
- }
+ return if !$self->{max_workers}; # if we have no workers we're done here
# if configured, leave children running on HUP
- return if $allow_open_children &&
- $self->{leave_children_open_on_reload};
+ 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
- }
+ # else send TERM to all (old and current) child workers
+ kill 15, (keys %{$self->{workers}}, keys %{$self->{old_workers}});
# nicely shutdown childs (give them max 10 seconds to shut down)
my $previous_alarm = alarm(10);
if ($restart && $self->{max_workers}) {
if (my $wpids = $ENV{PVE_DAEMON_WORKER_PIDS}) {
foreach my $pid (split(':', $wpids)) {
+ # check & untaint
if ($pid =~ m/^(\d+)$/) {
$self->{old_workers}->{$1} = 1;
}
syslog('info' , "starting server");
}
- POSIX::setsid();
+ POSIX::setsid();
open STDERR, '>&STDOUT' || die "can't close STDERR\n";
}
};
- eval {
+ eval {
if ($self->{max_workers}) {
my $old_sig_chld = $SIG{CHLD};
local $SIG{CHLD} = sub {
&$old_sig_chld(@_) if $old_sig_chld;
};
- # catch worker finished during restart phase
- &$finish_workers($self);
-
# now loop forever (until we receive terminate signal)
- for (;;) {
+ for (;;) {
&$start_workers($self);
sleep(5);
+ &$terminate_old_workers($self);
&$finish_workers($self);
last if $self->{terminate};
}
} else {
$self->run();
- }
+ }
};
my $err = $@;
eval {
my $class = ref($this) || $this;
- $self = bless {
+ $self = bless {
name => $name,
pidfile => "/var/run/${name}.pid",
workers => {},
die "unknown daemon option '$opt'\n";
}
}
-
+
# untaint
$self->{cmdline} = [map { /^(.*)$/ } @$cmdline];
}
if ($self->{max_workers}) {
- my @workers = keys %{$self->{workers}};
- push @workers, keys %{$self->{old_workers}};
+ my @workers = (keys %{$self->{workers}}, keys %{$self->{old_workers}});
$ENV{PVE_DAEMON_WORKER_PIDS} = join(':', @workers);
}
return 0 if !$pid_str;
return 0 if $pid_str !~ m/^(\d+)$/; # untaint
-
+
my $pid = int($1);
return $pid;
} else {
return 0;
}
-};
+};
sub running {
my ($self) = @_;
}
return undef;
- }});
+ }});
}
my $reload_daemon = sub {
if ($self->{env_restart_pve_daemon}) {
$self->start();
} else {
- my ($running, $pid) = $self->running();
+ my ($running, $pid) = $self->running();
if (!$running) {
$self->start();
} else {
}
return undef;
- }});
+ }});
}
sub register_reload_command {
&$reload_daemon($self, 1);
return undef;
- }});
+ }});
}
sub register_stop_command {
code => sub {
my ($param) = @_;
-
+
if (&$init_ppid()) {
$self->stop();
} else {
}
return undef;
- }});
+ }});
}
sub register_status_command {
additionalProperties => 0,
properties => {},
},
- returns => {
+ returns => {
type => 'string',
enum => ['stopped', 'running'],
},
if (defined($sockfd = $ENV{"PVE_DAEMON_SOCKET_$port"}) &&
$self->{env_restart_pve_daemon}) {
- die "unable to parse socket fd '$sockfd'\n"
+ die "unable to parse socket fd '$sockfd'\n"
if $sockfd !~ m/^(\d+)$/;
$sockfd = $1; # untaint
$socket = IO::Socket::IP->new;
- $socket->fdopen($sockfd, 'w') ||
+ $socket->fdopen($sockfd, 'w') ||
die "cannot fdopen file descriptor '$sockfd' - $!\n";
$socket->fcntl(Fcntl::F_SETFD(), Fcntl::FD_CLOEXEC);