From 41775167ac69a001f40c11e2ee9576887e17b8b4 Mon Sep 17 00:00:00 2001 From: Oliver Charles <ollie@ocharles.org.uk> Date: Sun, 2 Feb 2014 14:41:26 +0000 Subject: [PATCH] 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. --- .../activation/switch-to-configuration.pl | 55 ++++++++++++------- nixos/modules/system/activation/top-level.nix | 2 +- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index 33ae3aef9fca..91beed1130eb 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -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" diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index d9891f434cc8..30a529988a96 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -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"; };