add PVE::SystemD module

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2018-06-15 11:00:52 +02:00 committed by Thomas Lamprecht
parent 5a94ed9d9e
commit f024a872c4
3 changed files with 82 additions and 74 deletions

View File

@ -29,6 +29,7 @@ LIB_SOURCES = \
SectionConfig.pm \ SectionConfig.pm \
Subscription.pm \ Subscription.pm \
Syscall.pm \ Syscall.pm \
SystemD.pm \
Ticket.pm \ Ticket.pm \
Tools.pm Tools.pm

81
src/PVE/SystemD.pm Normal file
View File

@ -0,0 +1,81 @@
package PVE::SystemD;
use strict;
use warnings;
use Net::DBus qw(dbus_uint32 dbus_uint64);
use Net::DBus::Callback;
use Net::DBus::Reactor;
# NOTE: This calls the dbus main loop and must not be used when another dbus
# main loop is being used as we need to wait for the JobRemoved signal.
# Polling the job status instead doesn't work because this doesn't give us the
# distinction between success and failure.
#
# Note that the description is mandatory for security reasons.
sub enter_systemd_scope {
my ($unit, $description, %extra) = @_;
die "missing description\n" if !defined($description);
my $timeout = delete $extra{timeout};
$unit .= '.scope';
my $properties = [ [PIDs => [dbus_uint32($$)]] ];
foreach my $key (keys %extra) {
if ($key eq 'Slice' || $key eq 'KillMode') {
push @{$properties}, [$key, $extra{$key}];
} elsif ($key eq 'CPUShares') {
push @{$properties}, [$key, dbus_uint64($extra{$key})];
} elsif ($key eq 'CPUQuota') {
push @{$properties}, ['CPUQuotaPerSecUSec',
dbus_uint64($extra{$key} * 10_000)];
} else {
die "Don't know how to encode $key for systemd scope\n";
}
}
my $job;
my $done = 0;
my $bus = Net::DBus->system();
my $reactor = Net::DBus::Reactor->main();
my $service = $bus->get_service('org.freedesktop.systemd1');
my $if = $service->get_object('/org/freedesktop/systemd1', 'org.freedesktop.systemd1.Manager');
# Connect to the JobRemoved signal since we want to wait for it to finish
my $sigid;
my $timer;
my $cleanup = sub {
my ($no_shutdown) = @_;
$if->disconnect_from_signal('JobRemoved', $sigid) if defined($if);
$if = undef;
$sigid = undef;
$reactor->remove_timeout($timer) if defined($timer);
$timer = undef;
return if $no_shutdown;
$reactor->shutdown();
};
$sigid = $if->connect_to_signal('JobRemoved', sub {
my ($id, $removed_job, $signaled_unit, $result) = @_;
return if $signaled_unit ne $unit || $removed_job ne $job;
$cleanup->(0);
die "systemd job failed\n" if $result ne 'done';
$done = 1;
});
my $on_timeout = sub {
$cleanup->(0);
die "systemd job timed out\n";
};
$timer = $reactor->add_timeout($timeout * 1000, Net::DBus::Callback->new(method => $on_timeout))
if defined($timeout);
$job = $if->StartTransientUnit($unit, 'fail', $properties, []);
$reactor->run();
$cleanup->(1);
die "systemd job never completed\n" if !$done;
}
1;

View File

@ -24,9 +24,6 @@ use JSON;
use Text::ParseWords; use Text::ParseWords;
use String::ShellQuote; use String::ShellQuote;
use Time::HiRes qw(usleep gettimeofday tv_interval alarm); use Time::HiRes qw(usleep gettimeofday tv_interval alarm);
use Net::DBus qw(dbus_uint32 dbus_uint64);
use Net::DBus::Callback;
use Net::DBus::Reactor;
use Scalar::Util 'weaken'; use Scalar::Util 'weaken';
use PVE::Syscall; use PVE::Syscall;
@ -1544,77 +1541,6 @@ sub mkdirat($$$) {
return syscall(PVE::Syscall::mkdirat, $dirfd, $name, $mode) == 0; return syscall(PVE::Syscall::mkdirat, $dirfd, $name, $mode) == 0;
} }
# NOTE: This calls the dbus main loop and must not be used when another dbus
# main loop is being used as we need to wait for the JobRemoved signal.
# Polling the job status instead doesn't work because this doesn't give us the
# distinction between success and failure.
#
# Note that the description is mandatory for security reasons.
sub enter_systemd_scope {
my ($unit, $description, %extra) = @_;
die "missing description\n" if !defined($description);
my $timeout = delete $extra{timeout};
$unit .= '.scope';
my $properties = [ [PIDs => [dbus_uint32($$)]] ];
foreach my $key (keys %extra) {
if ($key eq 'Slice' || $key eq 'KillMode') {
push @$properties, [$key, $extra{$key}];
} elsif ($key eq 'CPUShares') {
push @$properties, [$key, dbus_uint64($extra{$key})];
} elsif ($key eq 'CPUQuota') {
push @$properties, ['CPUQuotaPerSecUSec',
dbus_uint64($extra{$key} * 10000)];
} else {
die "Don't know how to encode $key for systemd scope\n";
}
}
my $job;
my $done = 0;
my $bus = Net::DBus->system();
my $reactor = Net::DBus::Reactor->main();
my $service = $bus->get_service('org.freedesktop.systemd1');
my $if = $service->get_object('/org/freedesktop/systemd1', 'org.freedesktop.systemd1.Manager');
# Connect to the JobRemoved signal since we want to wait for it to finish
my $sigid;
my $timer;
my $cleanup = sub {
my ($no_shutdown) = @_;
$if->disconnect_from_signal('JobRemoved', $sigid) if defined($if);
$if = undef;
$sigid = undef;
$reactor->remove_timeout($timer) if defined($timer);
$timer = undef;
return if $no_shutdown;
$reactor->shutdown();
};
$sigid = $if->connect_to_signal('JobRemoved', sub {
my ($id, $removed_job, $signaled_unit, $result) = @_;
return if $signaled_unit ne $unit || $removed_job ne $job;
$cleanup->(0);
die "systemd job failed\n" if $result ne 'done';
$done = 1;
});
my $on_timeout = sub {
$cleanup->(0);
die "systemd job timed out\n";
};
$timer = $reactor->add_timeout($timeout * 1000, Net::DBus::Callback->new(method => $on_timeout))
if defined($timeout);
$job = $if->StartTransientUnit($unit, 'fail', $properties, []);
$reactor->run();
$cleanup->(1);
die "systemd job never completed\n" if !$done;
}
my $salt_starter = time(); my $salt_starter = time();
sub encrypt_pw { sub encrypt_pw {