switch-to-configuration.pl: Interact via DBus

Currently switch-to-configuration.pl uses system() calls to interact
with DBus. This can be error prone, especially when we are parsing
output that could change. In this commit, almost all calls to the
systemctl binary have been replaced with equivalent operations via DBus.
This commit is contained in:
Oliver Charles 2014-02-02 14:41:26 +00:00
parent f3ac3a265b
commit 41775167ac
2 changed files with 36 additions and 21 deletions

View file

@ -4,6 +4,7 @@ use strict;
use warnings;
use File::Basename;
use File::Slurp;
use Net::DBus;
use Sys::Syslog qw(:standard :macros);
use Cwd 'abs_path';
@ -59,17 +60,18 @@ syslog(LOG_NOTICE, "switching to system configuration $out");
# virtual console 1 and we restart the "tty1" unit.
$SIG{PIPE} = "IGNORE";
my $dbus = Net::DBus->find;
my $systemdService = $dbus->get_service('org.freedesktop.systemd1');
my $systemdManager = $systemdService->get_object('/org/freedesktop/systemd1');
sub getActiveUnits {
# FIXME: use D-Bus or whatever to query this, since parsing the
# output of list-units is likely to break.
my $lines = `@systemd@/bin/systemctl list-units --full`;
my $res = {};
foreach my $line (split '\n', $lines) {
chomp $line;
last if $line eq "";
$line =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s/ or next;
next if $1 eq "UNIT";
$res->{$1} = { load => $2, state => $3, substate => $4 };
foreach my $unit (@{ $systemdManager->ListUnits() }) {
$res->{$unit->[0]} = {
load => $unit->[2],
state => $unit->[3],
substate => $unit->[4]
};
}
return $res;
}
@ -277,7 +279,7 @@ foreach my $device (keys %$prevSwaps) {
if (scalar @unitsToStop > 0) {
@unitsToStop = unique(@unitsToStop);
print STDERR "stopping the following units: ", join(", ", sort(@unitsToStop)), "\n";
system("@systemd@/bin/systemctl", "stop", "--", @unitsToStop); # FIXME: ignore errors?
$systemdManager->StopUnit($_, "replace") for @unitsToStop;
}
print STDERR "NOT restarting the following units: ", join(", ", sort(@unitsToSkip)), "\n"
@ -292,21 +294,22 @@ system("$out/activate", "$out") == 0 or $res = 2;
# Restart systemd if necessary.
if (abs_path("/proc/1/exe") ne abs_path("@systemd@/lib/systemd/systemd")) {
print STDERR "restarting systemd...\n";
system("@systemd@/bin/systemctl", "daemon-reexec") == 0 or $res = 2;
$systemdManager->Reexecute();
}
# Forget about previously failed services.
system("@systemd@/bin/systemctl", "reset-failed");
$systemdManager->ResetFailed();
# Make systemd reload its units.
system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3;
# Make systemd reload its units
$systemdManager->Reload();
# Restart changed services (those that have to be restarted rather
# than stopped and started).
my @restart = unique(split('\n', read_file($restartListFile, err_mode => 'quiet') // ""));
if (scalar @restart > 0) {
print STDERR "restarting the following units: ", join(", ", sort(@restart)), "\n";
system("@systemd@/bin/systemctl", "restart", "--", @restart) == 0 or $res = 4;
$systemdManager->Restart($_, "replace") for @restart;
unlink($restartListFile);
}
@ -318,7 +321,7 @@ if (scalar @restart > 0) {
# systemd.
my @start = unique("default.target", "timers.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
print STDERR "starting the following units: ", join(", ", sort(@start)), "\n";
system("@systemd@/bin/systemctl", "start", "--", @start) == 0 or $res = 4;
$systemdManager->StartUnit($_, "replace") for @start;
unlink($startListFile);
# Reload units that need it. This includes remounting changed mount
@ -326,19 +329,31 @@ unlink($startListFile);
my @reload = unique(split '\n', read_file($reloadListFile, err_mode => 'quiet') // "");
if (scalar @reload > 0) {
print STDERR "reloading the following units: ", join(", ", sort(@reload)), "\n";
system("@systemd@/bin/systemctl", "reload", "--", @reload) == 0 or $res = 4;
$systemdManager->ReloadUnit($_, "replace") for @reload;
unlink($reloadListFile);
}
# Signal dbus to reload its configuration.
system("@systemd@/bin/systemctl", "reload", "dbus.service");
$systemdManager->ReloadUnit("dbus.service", "replace");
# Print failed and new units.
my (@failed, @new, @restarting);
my $activeNew = getActiveUnits;
while (my ($unit, $state) = each %{$activeNew}) {
push @failed, $unit if $state->{state} eq "failed" || $state->{substate} eq "auto-restart";
push @new, $unit if $state->{state} ne "failed" && !defined $activePrev->{$unit};
if ($state->{state} eq "failed") {
push @failed, $unit;
}
elsif ($state->{state} eq "auto-restart") {
# A unit in auto-restart state is a failure *if* it previously failed to start
my $unit = $systemdManager->GetUnit($unit);
if ($unit->ExecMainStatus ne '0') {
push @failed, $unit;
}
}
elsif ($state->{state} ne "failed" && !defined $activePrev->{$unit}) {
push @new, $unit;
}
}
print STDERR "the following new units were started: ", join(", ", sort(@new)), "\n"

View file

@ -108,7 +108,7 @@ let
configurationName = config.boot.loader.grub.configurationName;
# Needed by switch-to-configuration.
perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.XMLTwig}/lib/perl5/site_perl -I${pkgs.perlPackages.XMLParser}/lib/perl5/site_perl -I${pkgs.perlPackages.NetDBus}/lib/perl5/site_perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
};