diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index f0e39471eceee..eb620a3e3edc2 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -1860,15 +1860,6 @@ Superuser created successfully.
encapsulation.
-
-
- Changing systemd .socket units now restarts
- them and stops the service that is activated by them.
- Additionally, services with
- stopOnChange = false don’t break anymore
- when they are socket-activated.
-
-
The virtualisation.libvirtd module has been
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 3bdda82c026f4..c015147990388 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -520,8 +520,6 @@ In addition to numerous new and upgraded packages, this release has the followin
- `networking.sits` now supports Foo-over-UDP encapsulation.
-- Changing systemd `.socket` units now restarts them and stops the service that is activated by them. Additionally, services with `stopOnChange = false` don't break anymore when they are socket-activated.
-
- The `virtualisation.libvirtd` module has been refactored and updated with new options:
- `virtualisation.libvirtd.qemu*` options (e.g.: `virtualisation.libvirtd.qemuRunAsRoot`) were moved to [`virtualisation.libvirtd.qemu`](options.html#opt-virtualisation.libvirtd.qemu) submodule,
- software TPM1/TPM2 support (e.g.: Windows 11 guests) ([`virtualisation.libvirtd.qemu.swtpm`](options.html#opt-virtualisation.libvirtd.qemu.swtpm)),
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index e105502cf3a48..053496441d81c 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -11,6 +11,7 @@
my $out = "@out@";
+# FIXME: maybe we should use /proc/1/exe to get the current systemd.
my $curSystemd = abs_path("/run/current-system/sw/bin");
# To be robust against interruption, record what units need to be started etc.
@@ -18,16 +19,13 @@
my $restartListFile = "/run/nixos/restart-list";
my $reloadListFile = "/run/nixos/reload-list";
-# Parse restart/reload requests by the activation script.
-# Activation scripts may write newline-separated units to this
-# file and switch-to-configuration will handle them. While
-# `stopIfChanged = true` is ignored, switch-to-configuration will
-# handle `restartIfChanged = false` and `reloadIfChanged = true`.
-# This also works for socket-activated units.
+# Parse restart/reload requests by the activation script
my $restartByActivationFile = "/run/nixos/activation-restart-list";
+my $reloadByActivationFile = "/run/nixos/activation-reload-list";
my $dryRestartByActivationFile = "/run/nixos/dry-activation-restart-list";
+my $dryReloadByActivationFile = "/run/nixos/dry-activation-reload-list";
-make_path("/run/nixos", { mode => oct(755) });
+make_path("/run/nixos", { mode => 0755 });
my $action = shift @ARGV;
@@ -149,92 +147,6 @@ sub fingerprintUnit {
return abs_path($s) . (-f "${s}.d/overrides.conf" ? " " . abs_path "${s}.d/overrides.conf" : "");
}
-sub handleModifiedUnit {
- my ($unit, $baseName, $newUnitFile, $activePrev, $unitsToStop, $unitsToStart, $unitsToReload, $unitsToRestart, $unitsToSkip) = @_;
-
- if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target" || $unit =~ /\.slice$/ || $unit =~ /\.path$/) {
- # Do nothing. These cannot be restarted directly.
- # Slices and Paths don't have to be restarted since
- # properties (resource limits and inotify watches)
- # seem to get applied on daemon-reload.
- } elsif ($unit =~ /\.mount$/) {
- # Reload the changed mount unit to force a remount.
- $unitsToReload->{$unit} = 1;
- recordUnit($reloadListFile, $unit);
- } else {
- my $unitInfo = parseUnit($newUnitFile);
- if (boolIsTrue($unitInfo->{'X-ReloadIfChanged'} // "no")) {
- $unitsToReload->{$unit} = 1;
- recordUnit($reloadListFile, $unit);
- }
- elsif (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes") || boolIsTrue($unitInfo->{'RefuseManualStop'} // "no") || boolIsTrue($unitInfo->{'X-OnlyManualStart'} // "no")) {
- $unitsToSkip->{$unit} = 1;
- } else {
- # If this unit is socket-activated, then stop it instead
- # of restarting it to make sure the new version of it is
- # socket-activated.
- my $socketActivated = 0;
- if ($unit =~ /\.service$/) {
- my @sockets = split / /, ($unitInfo->{Sockets} // "");
- if (scalar @sockets == 0) {
- @sockets = ("$baseName.socket");
- }
- foreach my $socket (@sockets) {
- if (-e "$out/etc/systemd/system/$socket") {
- $socketActivated = 1;
- $unitsToStop->{$unit} = 1;
- # If the socket was not running previously,
- # start it now.
- if (not defined $activePrev->{$socket}) {
- $unitsToStart->{$socket} = 1;
- }
- }
- }
- }
-
- # Don't do the rest of this for socket-activated units
- # because we handled these above where we stop the unit.
- # Since only services can be socket-activated, the
- # following condition always evaluates to `true` for
- # non-service units.
- if ($socketActivated) {
- return;
- }
-
- # If we are restarting a socket, also stop the corresponding
- # service. This is required because restarting a socket
- # when the service is already activated fails.
- if ($unit =~ /\.socket$/) {
- my $service = $unitInfo->{Service} // "";
- if ($service eq "") {
- $service = "$baseName.service";
- }
- if (defined $activePrev->{$service}) {
- $unitsToStop->{$service} = 1;
- }
- $unitsToRestart->{$unit} = 1;
- recordUnit($restartListFile, $unit);
- } else {
- # Always restart non-services instead of stopping and starting them
- # because it doesn't make sense to stop them with a config from
- # the old evaluation.
- if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "yes") || $unit !~ /\.service$/) {
- # This unit should be restarted instead of
- # stopped and started.
- $unitsToRestart->{$unit} = 1;
- recordUnit($restartListFile, $unit);
- } else {
- # We write to a file to ensure that the
- # service gets restarted if we're interrupted.
- $unitsToStart->{$unit} = 1;
- recordUnit($startListFile, $unit);
- $unitsToStop->{$unit} = 1;
- }
- }
- }
- }
-}
-
# Figure out what units need to be stopped, started, restarted or reloaded.
my (%unitsToStop, %unitsToSkip, %unitsToStart, %unitsToRestart, %unitsToReload);
@@ -307,7 +219,65 @@ sub handleModifiedUnit {
}
elsif (fingerprintUnit($prevUnitFile) ne fingerprintUnit($newUnitFile)) {
- handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, %unitsToSkip);
+ if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target") {
+ # Do nothing. These cannot be restarted directly.
+ } elsif ($unit =~ /\.mount$/) {
+ # Reload the changed mount unit to force a remount.
+ $unitsToReload{$unit} = 1;
+ recordUnit($reloadListFile, $unit);
+ } elsif ($unit =~ /\.socket$/ || $unit =~ /\.path$/ || $unit =~ /\.slice$/) {
+ # FIXME: do something?
+ } else {
+ my $unitInfo = parseUnit($newUnitFile);
+ if (boolIsTrue($unitInfo->{'X-ReloadIfChanged'} // "no")) {
+ $unitsToReload{$unit} = 1;
+ recordUnit($reloadListFile, $unit);
+ }
+ elsif (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes") || boolIsTrue($unitInfo->{'RefuseManualStop'} // "no") || boolIsTrue($unitInfo->{'X-OnlyManualStart'} // "no")) {
+ $unitsToSkip{$unit} = 1;
+ } else {
+ if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "yes")) {
+ # This unit should be restarted instead of
+ # stopped and started.
+ $unitsToRestart{$unit} = 1;
+ recordUnit($restartListFile, $unit);
+ } else {
+ # If this unit is socket-activated, then stop the
+ # socket unit(s) as well, and restart the
+ # socket(s) instead of the service.
+ my $socketActivated = 0;
+ if ($unit =~ /\.service$/) {
+ my @sockets = split / /, ($unitInfo->{Sockets} // "");
+ if (scalar @sockets == 0) {
+ @sockets = ("$baseName.socket");
+ }
+ foreach my $socket (@sockets) {
+ if (defined $activePrev->{$socket}) {
+ $unitsToStop{$socket} = 1;
+ # Only restart sockets that actually
+ # exist in new configuration:
+ if (-e "$out/etc/systemd/system/$socket") {
+ $unitsToStart{$socket} = 1;
+ recordUnit($startListFile, $socket);
+ $socketActivated = 1;
+ }
+ }
+ }
+ }
+
+ # If the unit is not socket-activated, record
+ # that this unit needs to be started below.
+ # We write this to a file to ensure that the
+ # service gets restarted if we're interrupted.
+ if (!$socketActivated) {
+ $unitsToStart{$unit} = 1;
+ recordUnit($startListFile, $unit);
+ }
+
+ $unitsToStop{$unit} = 1;
+ }
+ }
+ }
}
}
}
@@ -392,6 +362,8 @@ sub filterUnits {
}
my @unitsToStopFiltered = filterUnits(\%unitsToStop);
+my @unitsToStartFiltered = filterUnits(\%unitsToStart);
+
# Show dry-run actions.
if ($action eq "dry-activate") {
@@ -403,44 +375,21 @@ sub filterUnits {
print STDERR "would activate the configuration...\n";
system("$out/dry-activate", "$out");
- # Handle the activation script requesting the restart or reload of a unit.
- my %unitsToAlsoStop;
- my %unitsToAlsoSkip;
- foreach (split('\n', read_file($dryRestartByActivationFile, err_mode => 'quiet') // "")) {
- my $unit = $_;
- my $baseUnit = $unit;
- my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
-
- # Detect template instances.
- if (!-e $newUnitFile && $unit =~ /^(.*)@[^\.]*\.(.*)$/) {
- $baseUnit = "$1\@.$2";
- $newUnitFile = "$out/etc/systemd/system/$baseUnit";
- }
-
- my $baseName = $baseUnit;
- $baseName =~ s/\.[a-z]*$//;
+ $unitsToRestart{$_} = 1 foreach
+ split('\n', read_file($dryRestartByActivationFile, err_mode => 'quiet') // "");
- handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToAlsoStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, %unitsToAlsoSkip);
- }
- unlink($dryRestartByActivationFile);
-
- my @unitsToAlsoStopFiltered = filterUnits(\%unitsToAlsoStop);
- if (scalar(keys %unitsToAlsoStop) > 0) {
- print STDERR "would stop the following units as well: ", join(", ", @unitsToAlsoStopFiltered), "\n"
- if scalar @unitsToAlsoStopFiltered;
- }
-
- print STDERR "would NOT restart the following changed units as well: ", join(", ", sort(keys %unitsToAlsoSkip)), "\n"
- if scalar(keys %unitsToAlsoSkip) > 0;
+ $unitsToReload{$_} = 1 foreach
+ split('\n', read_file($dryReloadByActivationFile, err_mode => 'quiet') // "");
print STDERR "would restart systemd\n" if $restartSystemd;
- print STDERR "would reload the following units: ", join(", ", sort(keys %unitsToReload)), "\n"
- if scalar(keys %unitsToReload) > 0;
print STDERR "would restart the following units: ", join(", ", sort(keys %unitsToRestart)), "\n"
if scalar(keys %unitsToRestart) > 0;
- my @unitsToStartFiltered = filterUnits(\%unitsToStart);
print STDERR "would start the following units: ", join(", ", @unitsToStartFiltered), "\n"
if scalar @unitsToStartFiltered;
+ print STDERR "would reload the following units: ", join(", ", sort(keys %unitsToReload)), "\n"
+ if scalar(keys %unitsToReload) > 0;
+ unlink($dryRestartByActivationFile);
+ unlink($dryReloadByActivationFile);
exit 0;
}
@@ -451,7 +400,7 @@ sub filterUnits {
print STDERR "stopping the following units: ", join(", ", @unitsToStopFiltered), "\n"
if scalar @unitsToStopFiltered;
# Use current version of systemctl binary before daemon is reexeced.
- system("$curSystemd/systemctl", "stop", "--", sort(keys %unitsToStop));
+ system("$curSystemd/systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
}
print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n"
@@ -465,38 +414,12 @@ sub filterUnits {
# Handle the activation script requesting the restart or reload of a unit.
# We can only restart and reload (not stop/start) because the units to be
-# stopped are already stopped before the activation script is run. We do however
-# make an exception for services that are socket-activated and that have to be stopped
-# instead of being restarted.
-my %unitsToAlsoStop;
-my %unitsToAlsoSkip;
-foreach (split('\n', read_file($restartByActivationFile, err_mode => 'quiet') // "")) {
- my $unit = $_;
- my $baseUnit = $unit;
- my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
-
- # Detect template instances.
- if (!-e $newUnitFile && $unit =~ /^(.*)@[^\.]*\.(.*)$/) {
- $baseUnit = "$1\@.$2";
- $newUnitFile = "$out/etc/systemd/system/$baseUnit";
- }
-
- my $baseName = $baseUnit;
- $baseName =~ s/\.[a-z]*$//;
-
- handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToAlsoStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, %unitsToAlsoSkip);
-}
-unlink($restartByActivationFile);
-
-my @unitsToAlsoStopFiltered = filterUnits(\%unitsToAlsoStop);
-if (scalar(keys %unitsToAlsoStop) > 0) {
- print STDERR "stopping the following units as well: ", join(", ", @unitsToAlsoStopFiltered), "\n"
- if scalar @unitsToAlsoStopFiltered;
- system("$curSystemd/systemctl", "stop", "--", sort(keys %unitsToAlsoStop));
-}
+# stopped are already stopped before the activation script is run.
+$unitsToRestart{$_} = 1 foreach
+ split('\n', read_file($restartByActivationFile, err_mode => 'quiet') // "");
-print STDERR "NOT restarting the following changed units as well: ", join(", ", sort(keys %unitsToAlsoSkip)), "\n"
- if scalar(keys %unitsToAlsoSkip) > 0;
+$unitsToReload{$_} = 1 foreach
+ split('\n', read_file($reloadByActivationFile, err_mode => 'quiet') // "");
# Restart systemd if necessary. Note that this is done using the
# current version of systemd, just in case the new one has trouble
@@ -537,40 +460,14 @@ sub filterUnits {
print STDERR "reloading the following units: ", join(", ", sort(keys %unitsToReload)), "\n";
system("@systemd@/bin/systemctl", "reload", "--", sort(keys %unitsToReload)) == 0 or $res = 4;
unlink($reloadListFile);
+ unlink($reloadByActivationFile);
}
# Restart changed services (those that have to be restarted rather
# than stopped and started).
if (scalar(keys %unitsToRestart) > 0) {
print STDERR "restarting the following units: ", join(", ", sort(keys %unitsToRestart)), "\n";
-
- # We split the units to be restarted into sockets and non-sockets.
- # This is because restarting sockets may fail which is not bad by
- # itself but which will prevent changes on the sockets. We usually
- # restart the socket and stop the service before that. Restarting
- # the socket will fail however when the service was re-activated
- # in the meantime. There is no proper way to prevent that from happening.
- my @unitsWithErrorHandling = grep { $_ !~ /\.socket$/ } sort(keys %unitsToRestart);
- my @unitsWithoutErrorHandling = grep { $_ =~ /\.socket$/ } sort(keys %unitsToRestart);
-
- if (scalar(@unitsWithErrorHandling) > 0) {
- system("@systemd@/bin/systemctl", "restart", "--", @unitsWithErrorHandling) == 0 or $res = 4;
- }
- if (scalar(@unitsWithoutErrorHandling) > 0) {
- # Don't print warnings from systemctl
- no warnings 'once';
- open(OLDERR, ">&", \*STDERR);
- close(STDERR);
-
- my $ret = system("@systemd@/bin/systemctl", "restart", "--", @unitsWithoutErrorHandling);
-
- # Print stderr again
- open(STDERR, ">&OLDERR");
-
- if ($ret ne 0) {
- print STDERR "warning: some sockets failed to restart. Please check your journal (journalctl -eb) and act accordingly.\n";
- }
- }
+ system("@systemd@/bin/systemctl", "restart", "--", sort(keys %unitsToRestart)) == 0 or $res = 4;
unlink($restartListFile);
unlink($restartByActivationFile);
}
@@ -581,7 +478,6 @@ sub filterUnits {
# that are symlinks to other units. We shouldn't start both at the
# same time because we'll get a "Failed to add path to set" error from
# systemd.
-my @unitsToStartFiltered = filterUnits(\%unitsToStart);
print STDERR "starting the following units: ", join(", ", @unitsToStartFiltered), "\n"
if scalar @unitsToStartFiltered;
system("@systemd@/bin/systemctl", "start", "--", sort(keys %unitsToStart)) == 0 or $res = 4;
@@ -589,7 +485,7 @@ sub filterUnits {
# Print failed and new units.
-my (@failed, @new);
+my (@failed, @new, @restarting);
my $activeNew = getActiveUnits;
while (my ($unit, $state) = each %{$activeNew}) {
if ($state->{state} eq "failed") {
@@ -605,9 +501,7 @@ sub filterUnits {
push @failed, $unit;
}
}
- # Ignore scopes since they are not managed by this script but rather
- # created and managed by third-party services via the systemd dbus API.
- elsif ($state->{state} ne "failed" && !defined $activePrev->{$unit} && $unit !~ /\.scope$/) {
+ elsif ($state->{state} ne "failed" && !defined $activePrev->{$unit}) {
push @new, $unit;
}
}
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 68da910d29cc8..026fd1791d33f 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -84,13 +84,6 @@ let
export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
chmod +x $out/bin/switch-to-configuration
- ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
- if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
- echo "switch-to-configuration syntax is not valid:"
- echo "$output"
- exit 1
- fi
- ''}
echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix
index 7ea07a390b808..78adf7ffa7da5 100644
--- a/nixos/tests/switch-test.nix
+++ b/nixos/tests/switch-test.nix
@@ -7,224 +7,15 @@ import ./make-test-python.nix ({ pkgs, ...} : {
};
nodes = {
- machine = { config, pkgs, lib, ... }: {
- environment.systemPackages = [ pkgs.socat ]; # for the socket activation stuff
+ machine = { ... }: {
users.mutableUsers = false;
-
- specialisation = {
- # A system with a simple socket-activated unit
- simple-socket.configuration = {
- systemd.services.socket-activated.serviceConfig = {
- ExecStart = pkgs.writeScript "socket-test.py" /* python */ ''
- #!${pkgs.python3}/bin/python3
-
- from socketserver import TCPServer, StreamRequestHandler
- import socket
-
- class Handler(StreamRequestHandler):
- def handle(self):
- self.wfile.write("hello".encode("utf-8"))
-
- class Server(TCPServer):
- def __init__(self, server_address, handler_cls):
- # Invoke base but omit bind/listen steps (performed by systemd activation!)
- TCPServer.__init__(
- self, server_address, handler_cls, bind_and_activate=False)
- # Override socket
- self.socket = socket.fromfd(3, self.address_family, self.socket_type)
-
- if __name__ == "__main__":
- server = Server(("localhost", 1234), Handler)
- server.serve_forever()
- '';
- };
- systemd.sockets.socket-activated = {
- wantedBy = [ "sockets.target" ];
- listenStreams = [ "/run/test.sock" ];
- socketConfig.SocketMode = lib.mkDefault "0777";
- };
- };
-
- # The same system but the socket is modified
- modified-socket.configuration = {
- imports = [ config.specialisation.simple-socket.configuration ];
- systemd.sockets.socket-activated.socketConfig.SocketMode = "0666";
- };
-
- # The same system but the service is modified
- modified-service.configuration = {
- imports = [ config.specialisation.simple-socket.configuration ];
- systemd.services.socket-activated.serviceConfig.X-Test = "test";
- };
-
- # The same system but both service and socket are modified
- modified-service-and-socket.configuration = {
- imports = [ config.specialisation.simple-socket.configuration ];
- systemd.services.socket-activated.serviceConfig.X-Test = "some_value";
- systemd.sockets.socket-activated.socketConfig.SocketMode = "0444";
- };
-
- # A system with a socket-activated service and some simple services
- service-and-socket.configuration = {
- imports = [ config.specialisation.simple-socket.configuration ];
- systemd.services.simple-service = {
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- ExecStart = "${pkgs.coreutils}/bin/true";
- };
- };
-
- systemd.services.simple-restart-service = {
- stopIfChanged = false;
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- ExecStart = "${pkgs.coreutils}/bin/true";
- };
- };
-
- systemd.services.simple-reload-service = {
- reloadIfChanged = true;
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- ExecStart = "${pkgs.coreutils}/bin/true";
- ExecReload = "${pkgs.coreutils}/bin/true";
- };
- };
-
- systemd.services.no-restart-service = {
- restartIfChanged = false;
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- ExecStart = "${pkgs.coreutils}/bin/true";
- };
- };
- };
-
- # The same system but with an activation script that restarts all services
- restart-and-reload-by-activation-script.configuration = {
- imports = [ config.specialisation.service-and-socket.configuration ];
- system.activationScripts.restart-and-reload-test = {
- supportsDryActivation = true;
- deps = [];
- text = ''
- if [ "$NIXOS_ACTION" = dry-activate ]; then
- f=/run/nixos/dry-activation-restart-list
- else
- f=/run/nixos/activation-restart-list
- fi
- cat <> "$f"
- simple-service.service
- simple-restart-service.service
- simple-reload-service.service
- no-restart-service.service
- socket-activated.service
- EOF
- '';
- };
- };
-
- # A system with a timer
- with-timer.configuration = {
- systemd.timers.test-timer = {
- wantedBy = [ "timers.target" ];
- timerConfig.OnCalendar = "@1395716396"; # chosen by fair dice roll
- };
- systemd.services.test-timer = {
- serviceConfig = {
- Type = "oneshot";
- ExecStart = "${pkgs.coreutils}/bin/true";
- };
- };
- };
-
- # The same system but with another time
- with-timer-modified.configuration = {
- imports = [ config.specialisation.with-timer.configuration ];
- systemd.timers.test-timer.timerConfig.OnCalendar = lib.mkForce "Fri 2012-11-23 16:00:00";
- };
-
- # A system with a systemd mount
- with-mount.configuration = {
- systemd.mounts = [
- {
- description = "Testmount";
- what = "tmpfs";
- type = "tmpfs";
- where = "/testmount";
- options = "size=1M";
- wantedBy = [ "local-fs.target" ];
- }
- ];
- };
-
- # The same system but with another time
- with-mount-modified.configuration = {
- systemd.mounts = [
- {
- description = "Testmount";
- what = "tmpfs";
- type = "tmpfs";
- where = "/testmount";
- options = "size=10M";
- wantedBy = [ "local-fs.target" ];
- }
- ];
- };
-
- # A system with a path unit
- with-path.configuration = {
- systemd.paths.test-watch = {
- wantedBy = [ "paths.target" ];
- pathConfig.PathExists = "/testpath";
- };
- systemd.services.test-watch = {
- serviceConfig = {
- Type = "oneshot";
- ExecStart = "${pkgs.coreutils}/bin/touch /testpath-modified";
- };
- };
- };
-
- # The same system but watching another file
- with-path-modified.configuration = {
- imports = [ config.specialisation.with-path.configuration ];
- systemd.paths.test-watch.pathConfig.PathExists = lib.mkForce "/testpath2";
- };
-
- # A system with a slice
- with-slice.configuration = {
- systemd.slices.testslice.sliceConfig.MemoryMax = "1"; # don't allow memory allocation
- systemd.services.testservice = {
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- ExecStart = "${pkgs.coreutils}/bin/true";
- Slice = "testslice.slice";
- };
- };
- };
-
- # The same system but the slice allows to allocate memory
- with-slice-non-crashing.configuration = {
- imports = [ config.specialisation.with-slice.configuration ];
- systemd.slices.testslice.sliceConfig.MemoryMax = lib.mkForce null;
- };
- };
};
other = { ... }: {
users.mutableUsers = true;
};
};
- testScript = { nodes, ... }: let
+ testScript = {nodes, ...}: let
originalSystem = nodes.machine.config.system.build.toplevel;
otherSystem = nodes.other.config.system.build.toplevel;
@@ -236,183 +27,12 @@ import ./make-test-python.nix ({ pkgs, ...} : {
set -o pipefail
exec env -i "$@" | tee /dev/stderr
'';
- in /* python */ ''
- def switch_to_specialisation(name, action="test"):
- out = machine.succeed(f"${originalSystem}/specialisation/{name}/bin/switch-to-configuration {action} 2>&1")
- assert_lacks(out, "switch-to-configuration line") # Perl warnings
- return out
-
- def assert_contains(haystack, needle):
- if needle not in haystack:
- print("The haystack that will cause the following exception is:")
- print("---")
- print(haystack)
- print("---")
- raise Exception(f"Expected string '{needle}' was not found")
-
- def assert_lacks(haystack, needle):
- if needle in haystack:
- print("The haystack that will cause the following exception is:")
- print("---")
- print(haystack, end="")
- print("---")
- raise Exception(f"Unexpected string '{needle}' was found")
-
-
+ in ''
machine.succeed(
"${stderrRunner} ${originalSystem}/bin/switch-to-configuration test"
)
machine.succeed(
"${stderrRunner} ${otherSystem}/bin/switch-to-configuration test"
)
-
- with subtest("systemd sockets"):
- machine.succeed("${originalSystem}/bin/switch-to-configuration test")
-
- # Simple socket is created
- out = switch_to_specialisation("simple-socket")
- assert_lacks(out, "stopping the following units:")
- # not checking for reload because dbus gets reloaded
- assert_lacks(out, "restarting the following units:")
- assert_lacks(out, "\nstarting the following units:")
- assert_contains(out, "the following new units were started: socket-activated.socket\n")
- assert_lacks(out, "as well:")
- machine.succeed("[ $(stat -c%a /run/test.sock) = 777 ]")
-
- # Changing the socket restarts it
- out = switch_to_specialisation("modified-socket")
- assert_lacks(out, "stopping the following units:")
- #assert_lacks(out, "reloading the following units:")
- assert_contains(out, "restarting the following units: socket-activated.socket\n")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
- machine.succeed("[ $(stat -c%a /run/test.sock) = 666 ]") # change was applied
-
- # The unit is properly activated when the socket is accessed
- if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello":
- raise Exception("Socket was not properly activated")
-
- # Changing the socket restarts it and ignores the active service
- out = switch_to_specialisation("simple-socket")
- assert_contains(out, "stopping the following units: socket-activated.service\n")
- assert_lacks(out, "reloading the following units:")
- assert_contains(out, "restarting the following units: socket-activated.socket\n")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
- machine.succeed("[ $(stat -c%a /run/test.sock) = 777 ]") # change was applied
-
- # Changing the service does nothing when the service is not active
- out = switch_to_specialisation("modified-service")
- assert_lacks(out, "stopping the following units:")
- assert_lacks(out, "reloading the following units:")
- assert_lacks(out, "restarting the following units:")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
-
- # Activating the service and modifying it stops it but leaves the socket untouched
- machine.succeed("socat - UNIX-CONNECT:/run/test.sock")
- out = switch_to_specialisation("simple-socket")
- assert_contains(out, "stopping the following units: socket-activated.service\n")
- assert_lacks(out, "reloading the following units:")
- assert_lacks(out, "restarting the following units:")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
-
- # Activating the service and both the service and the socket stops the service and restarts the socket
- machine.succeed("socat - UNIX-CONNECT:/run/test.sock")
- out = switch_to_specialisation("modified-service-and-socket")
- assert_contains(out, "stopping the following units: socket-activated.service\n")
- assert_lacks(out, "reloading the following units:")
- assert_contains(out, "restarting the following units: socket-activated.socket\n")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
-
- with subtest("restart and reload by activation file"):
- out = switch_to_specialisation("service-and-socket")
- # Switch to a system where the example services get restarted
- # by the activation script
- out = switch_to_specialisation("restart-and-reload-by-activation-script")
- assert_lacks(out, "stopping the following units:")
- assert_contains(out, "stopping the following units as well: simple-service.service, socket-activated.service\n")
- assert_contains(out, "reloading the following units: simple-reload-service.service\n")
- assert_contains(out, "restarting the following units: simple-restart-service.service\n")
- assert_contains(out, "\nstarting the following units: simple-service.service")
-
- # The same, but in dry mode
- switch_to_specialisation("service-and-socket")
- out = switch_to_specialisation("restart-and-reload-by-activation-script", action="dry-activate")
- assert_lacks(out, "would stop the following units:")
- assert_contains(out, "would stop the following units as well: simple-service.service, socket-activated.service\n")
- assert_contains(out, "would reload the following units: simple-reload-service.service\n")
- assert_contains(out, "would restart the following units: simple-restart-service.service\n")
- assert_contains(out, "\nwould start the following units: simple-service.service")
-
- with subtest("mounts"):
- switch_to_specialisation("with-mount")
- out = machine.succeed("mount | grep 'on /testmount'")
- assert_contains(out, "size=1024k")
-
- out = switch_to_specialisation("with-mount-modified")
- assert_lacks(out, "stopping the following units:")
- assert_contains(out, "reloading the following units: testmount.mount\n")
- assert_lacks(out, "restarting the following units:")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
- # It changed
- out = machine.succeed("mount | grep 'on /testmount'")
- assert_contains(out, "size=10240k")
-
- with subtest("timers"):
- switch_to_specialisation("with-timer")
- out = machine.succeed("systemctl show test-timer.timer")
- assert_contains(out, "OnCalendar=2014-03-25 02:59:56 UTC")
-
- out = switch_to_specialisation("with-timer-modified")
- assert_lacks(out, "stopping the following units:")
- assert_lacks(out, "reloading the following units:")
- assert_contains(out, "restarting the following units: test-timer.timer\n")
- assert_lacks(out, "\nstarting the following units:")
- assert_lacks(out, "the following new units were started:")
- assert_lacks(out, "as well:")
- # It changed
- out = machine.succeed("systemctl show test-timer.timer")
- assert_contains(out, "OnCalendar=Fri 2012-11-23 16:00:00")
-
- with subtest("paths"):
- switch_to_specialisation("with-path")
- machine.fail("test -f /testpath-modified")
-
- # touch the file, unit should be triggered
- machine.succeed("touch /testpath")
- machine.wait_until_succeeds("test -f /testpath-modified")
-
- machine.succeed("rm /testpath")
- machine.succeed("rm /testpath-modified")
- switch_to_specialisation("with-path-modified")
-
- machine.succeed("touch /testpath")
- machine.fail("test -f /testpath-modified")
- machine.succeed("touch /testpath2")
- machine.wait_until_succeeds("test -f /testpath-modified")
-
- # This test ensures that changes to slice configuration get applied.
- # We test this by having a slice that allows no memory allocation at
- # all and starting a service within it. If the service crashes, the slice
- # is applied and if we modify the slice to allow memory allocation, the
- # service should successfully start.
- with subtest("slices"):
- machine.succeed("echo 0 > /proc/sys/vm/panic_on_oom") # allow OOMing
- out = switch_to_specialisation("with-slice")
- machine.fail("systemctl start testservice.service")
- out = switch_to_specialisation("with-slice-non-crashing")
- machine.succeed("systemctl start testservice.service")
- machine.succeed("echo 1 > /proc/sys/vm/panic_on_oom") # disallow OOMing
-
'';
})