diff --git a/modules/services/networking/dhcpcd.nix b/modules/services/networking/dhcpcd.nix index 2a0d73f6004..cf7f621a85d 100644 --- a/modules/services/networking/dhcpcd.nix +++ b/modules/services/networking/dhcpcd.nix @@ -97,6 +97,11 @@ in wantedBy = [ "network.target" ]; + # Stopping dhcpcd during a reconfiguration is undesirable + # because it brings down the network interfaces configured by + # dhcpcd. So do a "systemctl restart" instead. + stopIfChanged = false; + path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; serviceConfig = diff --git a/modules/services/networking/ssh/sshd.nix b/modules/services/networking/ssh/sshd.nix index 21f81152fa5..8f898ce06a1 100644 --- a/modules/services/networking/ssh/sshd.nix +++ b/modules/services/networking/ssh/sshd.nix @@ -267,6 +267,8 @@ in wantedBy = [ "multi-user.target" ]; + stopIfChanged = false; + path = [ pkgs.openssh ]; environment.LD_LIBRARY_PATH = nssModulesPath; diff --git a/modules/system/activation/switch-to-configuration.pl b/modules/system/activation/switch-to-configuration.pl index b085778f099..28ccc158f72 100644 --- a/modules/system/activation/switch-to-configuration.pl +++ b/modules/system/activation/switch-to-configuration.pl @@ -6,6 +6,7 @@ use File::Basename; use File::Slurp; use Cwd 'abs_path'; +my $startListFile = "/run/systemd/start-list"; my $restartListFile = "/run/systemd/restart-list"; my $reloadListFile = "/run/systemd/reload-list"; @@ -125,7 +126,7 @@ while (my ($unit, $state) = each %{$activePrev}) { if ($unit ne "suspend.target" && $unit ne "hibernate.target") { my $unitInfo = parseUnit($newUnitFile); unless (boolIsTrue($unitInfo->{'RefuseManualStart'} // "false")) { - write_file($restartListFile, { append => 1 }, "$unit\n"); + write_file($startListFile, { append => 1 }, "$unit\n"); } } } @@ -158,21 +159,31 @@ while (my ($unit, $state) = each %{$activePrev}) { foreach my $socket (@sockets) { if (defined $activePrev->{$socket}) { push @unitsToStop, $socket; - write_file($restartListFile, { append => 1 }, "$socket\n"); + write_file($startListFile, { append => 1 }, "$socket\n"); $socketActivated = 1; } } } - # Otherwise, 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) { - write_file($restartListFile, { append => 1 }, "$unit\n"); - } + if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "true")) { - push @unitsToStop, $unit; + # This unit should be restarted instead of + # stopped and started. + write_file($restartListFile, { append => 1 }, "$unit\n"); + + } else { + + # 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) { + write_file($startListFile, { append => 1 }, "$unit\n"); + } + + push @unitsToStop, $unit; + + } } } } @@ -216,7 +227,7 @@ foreach my $mountPoint (keys %$prevFss) { push @unitsToStop, $unit; } elsif ($prev->{fsType} ne $new->{fsType} || $prev->{device} ne $new->{device}) { # Filesystem type or device changed, so unmount and mount it. - write_file($restartListFile, { append => 1 }, "$unit\n"); + write_file($startListFile, { append => 1 }, "$unit\n"); push @unitsToStop, $unit; } elsif ($prev->{options} ne $new->{options}) { # Mount options changes, so remount it. @@ -266,16 +277,25 @@ system("@systemd@/bin/systemctl", "reset-failed"); # Make systemd reload its units. system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3; +# 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; + unlink($restartListFile); +} + # Start all active targets, as well as changed units we stopped above. # The latter is necessary because some may not be dependencies of the # targets (i.e., they were manually started). FIXME: detect units # 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 @start = unique("default.target", split('\n', read_file($restartListFile, err_mode => 'quiet') // "")); +my @start = unique("default.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; -unlink($restartListFile); +unlink($startListFile); # Reload units that need it. This includes remounting changed mount # units. diff --git a/modules/system/boot/systemd-unit-options.nix b/modules/system/boot/systemd-unit-options.nix index ad9b5da2316..1f8097ada1c 100644 --- a/modules/system/boot/systemd-unit-options.nix +++ b/modules/system/boot/systemd-unit-options.nix @@ -183,6 +183,21 @@ rec { ''; }; + stopIfChanged = mkOption { + type = types.bool; + default = true; + description = '' + If set, a changed unit is restarted by calling + systemctl stop in the old configuration, + then systemctl start in the new one. + Otherwise, it is restarted in a single step using + systemctl restart in the new configuration. + The latter is less correct because it runs the + ExecStop commands from the new + configuration. + ''; + }; + }; diff --git a/modules/system/boot/systemd.nix b/modules/system/boot/systemd.nix index f4d0655118e..a0462951d00 100644 --- a/modules/system/boot/systemd.nix +++ b/modules/system/boot/systemd.nix @@ -246,6 +246,7 @@ let ${let env = cfg.globalEnvironment // def.environment; in concatMapStrings (n: "Environment=${n}=${getAttr n env}\n") (attrNames env)} ${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"} + ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"} ${optionalString (def.preStart != "") '' ExecStartPre=${makeJobScript "${name}-pre-start" ''