X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FDaemon.pm;h=dcc4d340eef8b3c1cad36f9f224b79c9cbfca8ea;hp=264f8be5e2a840aa7969a47ef95fbda017d7cbc8;hb=e97f807c388c10250f442b1f16c5315df2ffc2af;hpb=b51b16e6f58de4cb385bd461d97866b3d94c93ec diff --git a/src/PVE/Daemon.pm b/src/PVE/Daemon.pm index 264f8be..dcc4d34 100644 --- a/src/PVE/Daemon.pm +++ b/src/PVE/Daemon.pm @@ -24,7 +24,7 @@ use PVE::INotify; use POSIX ":sys_wait_h"; use Fcntl ':flock'; use Socket qw(IPPROTO_TCP TCP_NODELAY SOMAXCONN); -use IO::Socket::INET; +use IO::Socket::IP; use Getopt::Long; use Time::HiRes qw (gettimeofday); @@ -34,6 +34,7 @@ use base qw(PVE::CLIHandler); $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; my $daemon_initialized = 0; # we only allow one instance +my $daemon_sockets = []; my $close_daemon_lock = sub { my ($self) = @_; @@ -145,11 +146,7 @@ my $start_workers = sub { 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; @@ -183,34 +180,28 @@ my $start_workers = sub { } }; +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); @@ -247,6 +238,60 @@ my $terminate_server = sub { } }; +sub setup { + my ($self) = @_; + + initlog($self->{name}); + + my $restart = $ENV{RESTART_PVE_DAEMON}; + delete $ENV{RESTART_PVE_DAEMON}; + $self->{env_restart_pve_daemon} = $restart; + + my $lockfd = $ENV{PVE_DAEMON_LOCK_FD}; + delete $ENV{PVE_DAEMON_LOCK_FD}; + if (defined($lockfd)) { + die "unable to parse lock fd '$lockfd'\n" + if $lockfd !~ m/^(\d+)$/; + $lockfd = $1; # untaint + } + $self->{env_pve_lock_fd} = $lockfd; + + die "please run as root\n" if !$restart && ($> != 0); + + die "can't create more that one PVE::Daemon" if $daemon_initialized; + $daemon_initialized = 1; + + PVE::INotify::inotify_init(); + + if (my $gidstr = $self->{setgid}) { + my $gid = getgrnam($gidstr) || die "getgrnam failed - $!\n"; + POSIX::setgid($gid) || die "setgid $gid failed - $!\n"; + $EGID = "$gid $gid"; # this calls setgroups + # just to be sure + die "detected strange gid\n" if !($GID eq "$gid $gid" && $EGID eq "$gid $gid"); + } + + if (my $uidstr = $self->{setuid}) { + my $uid = getpwnam($uidstr) || die "getpwnam failed - $!\n"; + POSIX::setuid($uid) || die "setuid $uid failed - $!\n"; + # just to be sure + die "detected strange uid\n" if !($UID == $uid && $EUID == $uid); + } + + 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; + } + } + } + } + + $self->{nodename} = PVE::INotify::nodename(); +} + my $server_run = sub { my ($self, $debug) = @_; @@ -341,13 +386,11 @@ my $server_run = 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 (;;) { &$start_workers($self); sleep(5); + &$terminate_old_workers($self); &$finish_workers($self); last if $self->{terminate}; } @@ -382,38 +425,14 @@ sub new { $name = 'daemon' if !$name; # should not happen - initlog($name); - my $self; eval { - - my $restart = $ENV{RESTART_PVE_DAEMON}; - delete $ENV{RESTART_PVE_DAEMON}; - - my $lockfd = $ENV{PVE_DAEMON_LOCK_FD}; - delete $ENV{PVE_DAEMON_LOCK_FD}; - - if (defined($lockfd)) { - die "unable to parse lock fd '$lockfd'\n" - if $lockfd !~ m/^(\d+)$/; - $lockfd = $1; # untaint - } - - die "please run as root\n" if !$restart && ($> != 0); - - die "can't create more that one PVE::Daemon" if $daemon_initialized; - $daemon_initialized = 1; - - PVE::INotify::inotify_init(); - my $class = ref($this) || $this; $self = bless { name => $name, pidfile => "/var/run/${name}.pid", - env_restart_pve_daemon => $restart, - env_pve_lock_fd => $lockfd, workers => {}, old_workers => {}, }, $class; @@ -440,39 +459,9 @@ sub new { } } - if (my $gidstr = $self->{setgid}) { - my $gid = getgrnam($gidstr) || die "getgrnam failed - $!\n"; - POSIX::setgid($gid) || die "setgid $gid failed - $!\n"; - $EGID = "$gid $gid"; # this calls setgroups - # just to be sure - die "detected strange gid\n" if !($GID eq "$gid $gid" && $EGID eq "$gid $gid"); - } - - if (my $uidstr = $self->{setuid}) { - my $uid = getpwnam($uidstr) || die "getpwnam failed - $!\n"; - POSIX::setuid($uid) || die "setuid $uid failed - $!\n"; - # just to be sure - die "detected strange uid\n" if !($UID == $uid && $EUID == $uid); - } - - if ($restart && $self->{max_workers}) { - if (my $wpids = $ENV{PVE_DAEMON_WORKER_PIDS}) { - foreach my $pid (split(':', $wpids)) { - if ($pid =~ m/^(\d+)$/) { - $self->{old_workers}->{$1} = 1; - } - } - } - } - - $self->{nodename} = PVE::INotify::nodename(); - $self->{cmdline} = []; - - foreach my $el (@$cmdline) { - $el =~ m/^(.*)$/; # untaint - push @{$self->{cmdline}}, $1; - } + # untaint + $self->{cmdline} = [map { /^(.*)$/ } @$cmdline]; $0 = $name; }; @@ -501,9 +490,12 @@ sub restart_daemon { $ENV{RESTART_PVE_DAEMON} = 1; + foreach my $ds (@$daemon_sockets) { + $ds->fcntl(Fcntl::F_SETFD(), 0); + } + 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); } @@ -555,7 +547,10 @@ sub run { sub start { my ($self, $debug) = @_; - eval { &$server_run($self, $debug); }; + eval { + $self->setup(); + &$server_run($self, $debug); + }; if (my $err = $@) { &$log_err("start failed - $err"); exit(-1); @@ -576,6 +571,16 @@ my $read_pid = sub { return $pid; }; +# checks if the process was started by systemd +my $init_ppid = sub { + + if (getppid() == 1) { + return 1; + } else { + return 0; + } +}; + sub running { my ($self) = @_; @@ -652,7 +657,11 @@ sub register_start_command { code => sub { my ($param) = @_; - $self->start($param->{debug}); + if (&$init_ppid() || $param->{debug}) { + $self->start($param->{debug}); + } else { + PVE::Tools::run_command(['systemctl', 'start', $self->{name}]); + } return undef; }}); @@ -698,7 +707,11 @@ sub register_restart_command { code => sub { my ($param) = @_; - &$reload_daemon($self, $use_hup); + if (&$init_ppid()) { + &$reload_daemon($self, $use_hup); + } else { + PVE::Tools::run_command(['systemctl', $use_hup ? 'reload-or-restart' : 'restart', $self->{name}]); + } return undef; }}); @@ -748,7 +761,11 @@ sub register_stop_command { code => sub { my ($param) = @_; - $self->stop(); + if (&$init_ppid()) { + $self->stop(); + } else { + PVE::Tools::run_command(['systemctl', 'stop', $self->{name}]); + } return undef; }}); @@ -782,7 +799,7 @@ sub register_status_command { # some useful helper sub create_reusable_socket { - my ($self, $port, $host) = @_; + my ($self, $port, $host, $family) = @_; die "no port specifed" if !$port; @@ -795,17 +812,20 @@ sub create_reusable_socket { if $sockfd !~ m/^(\d+)$/; $sockfd = $1; # untaint - $socket = IO::Socket::INET->new; + $socket = IO::Socket::IP->new; $socket->fdopen($sockfd, 'w') || die "cannot fdopen file descriptor '$sockfd' - $!\n"; + $socket->fcntl(Fcntl::F_SETFD(), Fcntl::FD_CLOEXEC); } else { - $socket = IO::Socket::INET->new( + $socket = IO::Socket::IP->new( LocalAddr => $host, LocalPort => $port, Listen => SOMAXCONN, + Family => $family, Proto => 'tcp', + GetAddrInfoFlags => 0, ReuseAddr => 1) || die "unable to create socket - $@\n"; @@ -816,9 +836,8 @@ sub create_reusable_socket { $ENV{"PVE_DAEMON_SOCKET_$port"} = $socket->fileno; } - # remove FD_CLOEXEC bit to reuse on exec - $socket->fcntl(Fcntl::F_SETFD(), 0); - + push @$daemon_sockets, $socket; + return $socket; }