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 warnings;
use File::Basename; use File::Basename;
use File::Slurp; use File::Slurp;
use Net::DBus;
use Sys::Syslog qw(:standard :macros); use Sys::Syslog qw(:standard :macros);
use Cwd 'abs_path'; 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. # virtual console 1 and we restart the "tty1" unit.
$SIG{PIPE} = "IGNORE"; $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 { 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 = {}; my $res = {};
foreach my $line (split '\n', $lines) { foreach my $unit (@{ $systemdManager->ListUnits() }) {
chomp $line; $res->{$unit->[0]} = {
last if $line eq ""; load => $unit->[2],
$line =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s/ or next; state => $unit->[3],
next if $1 eq "UNIT"; substate => $unit->[4]
$res->{$1} = { load => $2, state => $3, substate => $4 }; };
} }
return $res; return $res;
} }
@ -277,7 +279,7 @@ foreach my $device (keys %$prevSwaps) {
if (scalar @unitsToStop > 0) { if (scalar @unitsToStop > 0) {
@unitsToStop = unique(@unitsToStop); @unitsToStop = unique(@unitsToStop);
print STDERR "stopping the following units: ", join(", ", sort(@unitsToStop)), "\n"; 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" 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. # Restart systemd if necessary.
if (abs_path("/proc/1/exe") ne abs_path("@systemd@/lib/systemd/systemd")) { if (abs_path("/proc/1/exe") ne abs_path("@systemd@/lib/systemd/systemd")) {
print STDERR "restarting systemd...\n"; print STDERR "restarting systemd...\n";
system("@systemd@/bin/systemctl", "daemon-reexec") == 0 or $res = 2;
$systemdManager->Reexecute();
} }
# Forget about previously failed services. # Forget about previously failed services.
system("@systemd@/bin/systemctl", "reset-failed"); $systemdManager->ResetFailed();
# Make systemd reload its units. # Make systemd reload its units
system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3; $systemdManager->Reload();
# Restart changed services (those that have to be restarted rather # Restart changed services (those that have to be restarted rather
# than stopped and started). # than stopped and started).
my @restart = unique(split('\n', read_file($restartListFile, err_mode => 'quiet') // "")); my @restart = unique(split('\n', read_file($restartListFile, err_mode => 'quiet') // ""));
if (scalar @restart > 0) { if (scalar @restart > 0) {
print STDERR "restarting the following units: ", join(", ", sort(@restart)), "\n"; 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); unlink($restartListFile);
} }
@ -318,7 +321,7 @@ if (scalar @restart > 0) {
# systemd. # systemd.
my @start = unique("default.target", "timers.target", split('\n', read_file($startListFile, err_mode => 'quiet') // "")); 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"; 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); unlink($startListFile);
# Reload units that need it. This includes remounting changed mount # 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') // ""); my @reload = unique(split '\n', read_file($reloadListFile, err_mode => 'quiet') // "");
if (scalar @reload > 0) { if (scalar @reload > 0) {
print STDERR "reloading the following units: ", join(", ", sort(@reload)), "\n"; 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); unlink($reloadListFile);
} }
# Signal dbus to reload its configuration. # Signal dbus to reload its configuration.
system("@systemd@/bin/systemctl", "reload", "dbus.service"); $systemdManager->ReloadUnit("dbus.service", "replace");
# Print failed and new units. # Print failed and new units.
my (@failed, @new, @restarting); my (@failed, @new, @restarting);
my $activeNew = getActiveUnits; my $activeNew = getActiveUnits;
while (my ($unit, $state) = each %{$activeNew}) { while (my ($unit, $state) = each %{$activeNew}) {
push @failed, $unit if $state->{state} eq "failed" || $state->{substate} eq "auto-restart"; if ($state->{state} eq "failed") {
push @new, $unit if $state->{state} ne "failed" && !defined $activePrev->{$unit}; 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" 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; configurationName = config.boot.loader.grub.configurationName;
# Needed by switch-to-configuration. # 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";
}; };