about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/system
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/system')
-rw-r--r--nixpkgs/nixos/modules/system/activation/activation-script.nix9
-rw-r--r--nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl278
-rw-r--r--nixpkgs/nixos/modules/system/activation/top-level.nix17
-rw-r--r--nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/initrd-ssh.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/kernel.nix17
-rw-r--r--nixpkgs/nixos/modules/system/boot/kernel_config.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix12
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py13
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix18
-rw-r--r--nixpkgs/nixos/modules/system/boot/luksroot.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/networkd.nix42
-rw-r--r--nixpkgs/nixos/modules/system/boot/plymouth.nix3
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1.nix55
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd.nix10
-rw-r--r--nixpkgs/nixos/modules/system/etc/etc.nix9
17 files changed, 366 insertions, 127 deletions
diff --git a/nixpkgs/nixos/modules/system/activation/activation-script.nix b/nixpkgs/nixos/modules/system/activation/activation-script.nix
index 704fc15fe207..8dbfe393f109 100644
--- a/nixpkgs/nixos/modules/system/activation/activation-script.nix
+++ b/nixpkgs/nixos/modules/system/activation/activation-script.nix
@@ -110,7 +110,7 @@ in
     system.activationScripts = mkOption {
       default = {};
 
-      example = literalExample ''
+      example = literalExpression ''
         { stdio.text =
           '''
             # Needed by some programs.
@@ -147,7 +147,7 @@ in
     system.userActivationScripts = mkOption {
       default = {};
 
-      example = literalExample ''
+      example = literalExpression ''
         { plasmaSetup = {
             text = '''
               ${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5"
@@ -193,9 +193,8 @@ in
 
     environment.usrbinenv = mkOption {
       default = "${pkgs.coreutils}/bin/env";
-      example = literalExample ''
-        "''${pkgs.busybox}/bin/env"
-      '';
+      defaultText = literalExpression ''"''${pkgs.coreutils}/bin/env"'';
+      example = literalExpression ''"''${pkgs.busybox}/bin/env"'';
       type = types.nullOr types.path;
       visible = false;
       description = ''
diff --git a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
index b7a062755296..e105502cf3a4 100644
--- a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
@@ -2,6 +2,7 @@
 
 use strict;
 use warnings;
+use File::Path qw(make_path);
 use File::Basename;
 use File::Slurp;
 use Net::DBus;
@@ -10,13 +11,23 @@ use Cwd 'abs_path';
 
 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.
-my $startListFile = "/run/systemd/start-list";
-my $restartListFile = "/run/systemd/restart-list";
-my $reloadListFile = "/run/systemd/reload-list";
+my $startListFile = "/run/nixos/start-list";
+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.
+my $restartByActivationFile = "/run/nixos/activation-restart-list";
+my $dryRestartByActivationFile = "/run/nixos/dry-activation-restart-list";
+
+make_path("/run/nixos", { mode => oct(755) });
 
 my $action = shift @ARGV;
 
@@ -138,6 +149,92 @@ 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);
 
@@ -150,7 +247,7 @@ $unitsToRestart{$_} = 1 foreach
     split('\n', read_file($restartListFile, err_mode => 'quiet') // "");
 
 $unitsToReload{$_} = 1 foreach
-    split '\n', read_file($reloadListFile, err_mode => 'quiet') // "";
+    split('\n', read_file($reloadListFile, err_mode => 'quiet') // "");
 
 my $activePrev = getActiveUnits;
 while (my ($unit, $state) = each %{$activePrev}) {
@@ -210,65 +307,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
         }
 
         elsif (fingerprintUnit($prevUnitFile) ne fingerprintUnit($newUnitFile)) {
-            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;
-                    }
-                }
-            }
+            handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, %unitsToSkip);
         }
     }
 }
@@ -353,8 +392,6 @@ sub filterUnits {
 }
 
 my @unitsToStopFiltered = filterUnits(\%unitsToStop);
-my @unitsToStartFiltered = filterUnits(\%unitsToStart);
-
 
 # Show dry-run actions.
 if ($action eq "dry-activate") {
@@ -366,13 +403,44 @@ if ($action eq "dry-activate") {
     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]*$//;
+
+        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;
+
     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;
     exit 0;
 }
 
@@ -383,7 +451,7 @@ if (scalar (keys %unitsToStop) > 0) {
     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)); # FIXME: ignore errors?
+    system("$curSystemd/systemctl", "stop", "--", sort(keys %unitsToStop));
 }
 
 print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n"
@@ -395,6 +463,41 @@ my $res = 0;
 print STDERR "activating the configuration...\n";
 system("$out/activate", "$out") == 0 or $res = 2;
 
+# 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));
+}
+
+print STDERR "NOT restarting the following changed units as well: ", join(", ", sort(keys %unitsToAlsoSkip)), "\n"
+    if scalar(keys %unitsToAlsoSkip) > 0;
+
 # Restart systemd if necessary. Note that this is done using the
 # current version of systemd, just in case the new one has trouble
 # communicating with the running pid 1.
@@ -440,8 +543,36 @@ if (scalar(keys %unitsToReload) > 0) {
 # than stopped and started).
 if (scalar(keys %unitsToRestart) > 0) {
     print STDERR "restarting the following units: ", join(", ", sort(keys %unitsToRestart)), "\n";
-    system("@systemd@/bin/systemctl", "restart", "--", sort(keys %unitsToRestart)) == 0 or $res = 4;
+
+    # 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";
+        }
+    }
     unlink($restartListFile);
+    unlink($restartByActivationFile);
 }
 
 # Start all active targets, as well as changed units we stopped above.
@@ -450,6 +581,7 @@ if (scalar(keys %unitsToRestart) > 0) {
 # 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;
@@ -457,7 +589,7 @@ unlink($startListFile);
 
 
 # Print failed and new units.
-my (@failed, @new, @restarting);
+my (@failed, @new);
 my $activeNew = getActiveUnits;
 while (my ($unit, $state) = each %{$activeNew}) {
     if ($state->{state} eq "failed") {
@@ -473,7 +605,9 @@ while (my ($unit, $state) = each %{$activeNew}) {
             push @failed, $unit;
         }
     }
-    elsif ($state->{state} ne "failed" && !defined $activePrev->{$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$/) {
         push @new, $unit;
     }
 }
diff --git a/nixpkgs/nixos/modules/system/activation/top-level.nix b/nixpkgs/nixos/modules/system/activation/top-level.nix
index dad9acba91af..68da910d29cc 100644
--- a/nixpkgs/nixos/modules/system/activation/top-level.nix
+++ b/nixpkgs/nixos/modules/system/activation/top-level.nix
@@ -84,6 +84,13 @@ 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
 
@@ -155,7 +162,7 @@ in
 
     specialisation = mkOption {
       default = {};
-      example = lib.literalExample "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
+      example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
       description = ''
         Additional configurations to build. If
         <literal>inheritParentConfig</literal> is true, the system
@@ -243,7 +250,7 @@ in
 
     system.replaceRuntimeDependencies = mkOption {
       default = [];
-      example = lib.literalExample "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]";
+      example = lib.literalExpression "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]";
       type = types.listOf (types.submodule (
         { ... }: {
           options.original = mkOption {
@@ -274,7 +281,11 @@ in
         if config.networking.hostName == ""
         then "unnamed"
         else config.networking.hostName;
-      defaultText = '''networking.hostName' if non empty else "unnamed"'';
+      defaultText = literalExpression ''
+        if config.networking.hostName == ""
+        then "unnamed"
+        else config.networking.hostName;
+      '';
       description = ''
         The name of the system used in the <option>system.build.toplevel</option> derivation.
         </para><para>
diff --git a/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix b/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix
index b35fb0b57c05..9b52d4bbdb1e 100644
--- a/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix
+++ b/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix
@@ -35,7 +35,7 @@ in
           </para>
         </warning>
       '';
-      example = "./configuration.ovpn";
+      example = literalExpression "./configuration.ovpn";
     };
 
   };
diff --git a/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix b/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
index 00ac83a18972..0999142de86e 100644
--- a/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
@@ -78,7 +78,7 @@ in
     authorizedKeys = mkOption {
       type = types.listOf types.str;
       default = config.users.users.root.openssh.authorizedKeys.keys;
-      defaultText = "config.users.users.root.openssh.authorizedKeys.keys";
+      defaultText = literalExpression "config.users.users.root.openssh.authorizedKeys.keys";
       description = ''
         Authorized keys for the root user on initrd.
       '';
diff --git a/nixpkgs/nixos/modules/system/boot/kernel.nix b/nixpkgs/nixos/modules/system/boot/kernel.nix
index 15a5fd236092..4a9da9394519 100644
--- a/nixpkgs/nixos/modules/system/boot/kernel.nix
+++ b/nixpkgs/nixos/modules/system/boot/kernel.nix
@@ -23,7 +23,7 @@ in
 
     boot.kernel.features = mkOption {
       default = {};
-      example = literalExample "{ debug = true; }";
+      example = literalExpression "{ debug = true; }";
       internal = true;
       description = ''
         This option allows to enable or disable certain kernel features.
@@ -46,8 +46,8 @@ in
       });
       # We don't want to evaluate all of linuxPackages for the manual
       # - some of it might not even evaluate correctly.
-      defaultText = "pkgs.linuxPackages";
-      example = literalExample "pkgs.linuxKernel.packages.linux_5_10";
+      defaultText = literalExpression "pkgs.linuxPackages";
+      example = literalExpression "pkgs.linuxKernel.packages.linux_5_10";
       description = ''
         This option allows you to override the Linux kernel used by
         NixOS.  Since things like external kernel module packages are
@@ -65,7 +65,7 @@ in
     boot.kernelPatches = mkOption {
       type = types.listOf types.attrs;
       default = [];
-      example = literalExample "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]";
+      example = literalExpression "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]";
       description = "A list of additional patches to apply to the kernel.";
     };
 
@@ -83,7 +83,10 @@ in
     };
 
     boot.kernelParams = mkOption {
-      type = types.listOf types.str;
+      type = types.listOf (types.strMatching ''([^"[:space:]]|"[^"]*")+'' // {
+        name = "kernelParam";
+        description = "string, with spaces inside double quotes";
+      });
       default = [ ];
       description = "Parameters added to the kernel command line.";
     };
@@ -113,7 +116,7 @@ in
     boot.extraModulePackages = mkOption {
       type = types.listOf types.package;
       default = [];
-      example = literalExample "[ config.boot.kernelPackages.nvidia_x11 ]";
+      example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]";
       description = "A list of additional packages supplying kernel modules.";
     };
 
@@ -181,7 +184,7 @@ in
 
     system.requiredKernelConfig = mkOption {
       default = [];
-      example = literalExample ''
+      example = literalExpression ''
         with config.lib.kernelConfig; [
           (isYes "MODULES")
           (isEnabled "FB_CON_DECOR")
diff --git a/nixpkgs/nixos/modules/system/boot/kernel_config.nix b/nixpkgs/nixos/modules/system/boot/kernel_config.nix
index 5d9534024b06..495fe74bc21e 100644
--- a/nixpkgs/nixos/modules/system/boot/kernel_config.nix
+++ b/nixpkgs/nixos/modules/system/boot/kernel_config.nix
@@ -100,7 +100,7 @@ in
 
     settings = mkOption {
       type = types.attrsOf kernelItem;
-      example = literalExample '' with lib.kernel; {
+      example = literalExpression '' with lib.kernel; {
         "9P_NET" = yes;
         USB = option yes;
         MMC_BLOCK_MINORS = freeform "32";
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix b/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
index 1be663670384..fa8500dd42bd 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
@@ -329,7 +329,7 @@ in
 
       extraInstallCommands = mkOption {
         default = "";
-        example = literalExample ''
+        example = ''
           # the example below generates detached signatures that GRUB can verify
           # https://www.gnu.org/software/grub/manual/grub/grub.html#Using-digital-signatures
           ''${pkgs.findutils}/bin/find /boot -not -path "/boot/efi/*" -type f -name '*.sig' -delete
@@ -392,7 +392,7 @@ in
       extraFiles = mkOption {
         type = types.attrsOf types.path;
         default = {};
-        example = literalExample ''
+        example = literalExpression ''
           { "memtest.bin" = "''${pkgs.memtest86plus}/memtest.bin"; }
         '';
         description = ''
@@ -413,7 +413,7 @@ in
 
       splashImage = mkOption {
         type = types.nullOr types.path;
-        example = literalExample "./my-background.png";
+        example = literalExpression "./my-background.png";
         description = ''
           Background image used for GRUB.
           Set to <literal>null</literal> to run GRUB in text mode.
@@ -449,7 +449,7 @@ in
 
       theme = mkOption {
         type = types.nullOr types.path;
-        example = literalExample "pkgs.nixos-grub2-theme";
+        example = literalExpression "pkgs.nixos-grub2-theme";
         default = null;
         description = ''
           Grub theme to be used.
@@ -475,7 +475,7 @@ in
       font = mkOption {
         type = types.nullOr types.path;
         default = "${realGrub}/share/grub/unicode.pf2";
-        defaultText = ''"''${pkgs.grub2}/share/grub/unicode.pf2"'';
+        defaultText = literalExpression ''"''${pkgs.grub2}/share/grub/unicode.pf2"'';
         description = ''
           Path to a TrueType, OpenType, or pf2 font to be used by Grub.
         '';
@@ -483,7 +483,7 @@ in
 
       fontSize = mkOption {
         type = types.nullOr types.int;
-        example = literalExample 16;
+        example = 16;
         default = null;
         description = ''
           Font size for the grub menu. Ignored unless <literal>font</literal>
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix b/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix
index 249c2761934d..ef8595592f41 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix
@@ -33,7 +33,7 @@ in
             booting from the GRUB boot menu.
           '';
         default = { };
-        example = literalExample ''
+        example = literalExpression ''
           { demo = '''
               #!ipxe
               dhcp
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 7134b4321630..6c26b4e0f87a 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -208,10 +208,15 @@ def main() -> None:
         if os.path.exists("@efiSysMountPoint@/loader/loader.conf"):
             os.unlink("@efiSysMountPoint@/loader/loader.conf")
 
-        if "@canTouchEfiVariables@" == "1":
-            subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"])
-        else:
-            subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "--no-variables", "install"])
+        flags = []
+
+        if "@canTouchEfiVariables@" != "1":
+            flags.append("--no-variables")
+
+        if "@graceful@" == "1":
+            flags.append("--graceful")
+
+        subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@"] + flags + ["install"])
     else:
         # Update bootloader to latest if needed
         systemd_version = subprocess.check_output(["@systemd@/bin/bootctl", "--version"], universal_newlines=True).split()[1]
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index ff304f570d35..0f76d7d6b24a 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -24,7 +24,7 @@ let
 
     configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit;
 
-    inherit (cfg) consoleMode;
+    inherit (cfg) consoleMode graceful;
 
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
 
@@ -126,6 +126,22 @@ in {
         '';
       };
     };
+
+    graceful = mkOption {
+      default = false;
+
+      type = types.bool;
+
+      description = ''
+        Invoke <literal>bootctl install</literal> with the <literal>--graceful</literal> option,
+        which ignores errors when EFI variables cannot be written or when the EFI System Partition
+        cannot be found. Currently only applies to random seed operations.
+
+        Only enable this option if <literal>systemd-boot</literal> otherwise fails to install, as the
+        scope or implication of the <literal>--graceful</literal> option may change in the future.
+      '';
+    };
+
   };
 
   config = mkIf cfg.enable {
diff --git a/nixpkgs/nixos/modules/system/boot/luksroot.nix b/nixpkgs/nixos/modules/system/boot/luksroot.nix
index f87d3b07a360..fb5269e43d08 100644
--- a/nixpkgs/nixos/modules/system/boot/luksroot.nix
+++ b/nixpkgs/nixos/modules/system/boot/luksroot.nix
@@ -663,13 +663,11 @@ in
                 };
 
                 encryptedPass = mkOption {
-                  default = "";
                   type = types.path;
                   description = "Path to the GPG encrypted passphrase.";
                 };
 
                 publicKey = mkOption {
-                  default = "";
                   type = types.path;
                   description = "Path to the Public Key.";
                 };
diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix
index bf254be1341b..662dfe2db989 100644
--- a/nixpkgs/nixos/modules/system/boot/networkd.nix
+++ b/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -250,6 +250,16 @@ let
         (assertRange "ERSPANIndex" 1 1048575)
       ];
 
+      sectionFooOverUDP = checkUnitConfig "FooOverUDP" [
+        (assertOnlyFields [
+          "Port"
+          "Encapsulation"
+          "Protocol"
+        ])
+        (assertPort "Port")
+        (assertValueOneOf "Encapsulation" ["FooOverUDP" "GenericUDPEncapsulation"])
+      ];
+
       sectionPeer = checkUnitConfig "Peer" [
         (assertOnlyFields [
           "Name"
@@ -668,6 +678,9 @@ let
           "SendOption"
           "UserClass"
           "VendorClass"
+          "DUIDType"
+          "DUIDRawData"
+          "IAID"
         ])
         (assertValueOneOf "UseAddress" boolValues)
         (assertValueOneOf "UseDNS" boolValues)
@@ -677,6 +690,7 @@ let
         (assertValueOneOf "ForceDHCPv6PDOtherInformation" boolValues)
         (assertValueOneOf "WithoutRA" ["solicit" "information-request"])
         (assertRange "SendOption" 1 65536)
+        (assertInt "IAID")
       ];
 
       sectionDHCPv6PrefixDelegation = checkUnitConfig "DHCPv6PrefixDelegation" [
@@ -844,7 +858,6 @@ let
     options = {
       wireguardPeerConfig = mkOption {
         default = {};
-        example = { };
         type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuardPeer;
         description = ''
           Each attribute in this set specifies an option in the
@@ -859,7 +872,6 @@ let
   netdevOptions = commonNetworkOptions // {
 
     netdevConfig = mkOption {
-      default = {};
       example = { Name = "mybridge"; Kind = "bridge"; };
       type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionNetdev;
       description = ''
@@ -896,7 +908,6 @@ let
 
     vxlanConfig = mkOption {
       default = {};
-      example = { Id = "4"; };
       type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVXLAN;
       description = ''
         Each attribute in this set specifies an option in the
@@ -918,6 +929,18 @@ let
       '';
     };
 
+    fooOverUDPConfig = mkOption {
+      default = { };
+      example = { Port = 9001; };
+      type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionFooOverUDP;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[FooOverUDP]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
     peerConfig = mkOption {
       default = {};
       example = { Name = "veth2"; };
@@ -959,7 +982,7 @@ let
       example = {
         PrivateKeyFile = "/etc/wireguard/secret.key";
         ListenPort = 51820;
-        FwMark = 42;
+        FirewallMark = 42;
       };
       type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuard;
       description = ''
@@ -1038,7 +1061,6 @@ let
   addressOptions = {
     options = {
       addressConfig = mkOption {
-        default = {};
         example = { Address = "192.168.0.100/24"; };
         type = types.addCheck (types.attrsOf unitOption) check.network.sectionAddress;
         description = ''
@@ -1055,7 +1077,7 @@ let
     options = {
       routingPolicyRuleConfig = mkOption {
         default = { };
-        example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; } ;};
+        example = { Table = 10; IncomingInterface = "eth1"; Family = "both"; };
         type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoutingPolicyRule;
         description = ''
           Each attribute in this set specifies an option in the
@@ -1146,7 +1168,7 @@ let
 
     dhcpV6Config = mkOption {
       default = {};
-      example = { UseDNS = true; UseRoutes = true; };
+      example = { UseDNS = true; };
       type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPv6;
       description = ''
         Each attribute in this set specifies an option in the
@@ -1213,7 +1235,7 @@ let
 
     ipv6Prefixes = mkOption {
       default = [];
-      example = { AddressAutoconfiguration = true; OnLink = true; };
+      example = [ { AddressAutoconfiguration = true; OnLink = true; } ];
       type = with types; listOf (submodule ipv6PrefixOptions);
       description = ''
         A list of ipv6Prefix sections to be added to the unit.  See
@@ -1449,6 +1471,10 @@ let
           [Tunnel]
           ${attrsToSection def.tunnelConfig}
         ''
+        + optionalString (def.fooOverUDPConfig != { }) ''
+          [FooOverUDP]
+          ${attrsToSection def.fooOverUDPConfig}
+        ''
         + optionalString (def.peerConfig != { }) ''
           [Peer]
           ${attrsToSection def.peerConfig}
diff --git a/nixpkgs/nixos/modules/system/boot/plymouth.nix b/nixpkgs/nixos/modules/system/boot/plymouth.nix
index 2a545e552513..4b8194d2f85c 100644
--- a/nixpkgs/nixos/modules/system/boot/plymouth.nix
+++ b/nixpkgs/nixos/modules/system/boot/plymouth.nix
@@ -62,6 +62,7 @@ in
 
       font = mkOption {
         default = "${pkgs.dejavu_fonts.minimal}/share/fonts/truetype/DejaVuSans.ttf";
+        defaultText = literalExpression ''"''${pkgs.dejavu_fonts.minimal}/share/fonts/truetype/DejaVuSans.ttf"'';
         type = types.path;
         description = ''
           Font file made available for displaying text on the splash screen.
@@ -88,7 +89,7 @@ in
         type = types.path;
         # Dimensions are 48x48 to match GDM logo
         default = "${nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png";
-        defaultText = ''pkgs.fetchurl {
+        defaultText = literalExpression ''pkgs.fetchurl {
           url = "https://nixos.org/logo/nixos-hires.png";
           sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
         }'';
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1.nix b/nixpkgs/nixos/modules/system/boot/stage-1.nix
index 03133fa1bc43..adbed9d8d58e 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-1.nix
@@ -137,6 +137,14 @@ let
         copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
       ''}
 
+      # Copy multipath.
+      ${optionalString config.services.multipath.enable ''
+        copy_bin_and_libs ${config.services.multipath.package}/bin/multipath
+        copy_bin_and_libs ${config.services.multipath.package}/bin/multipathd
+        # Copy lib/multipath manually.
+        cp -rpv ${config.services.multipath.package}/lib/multipath $out/lib
+      ''}
+
       # Copy secrets if needed.
       #
       # TODO: move out to a separate script; see #85000.
@@ -199,6 +207,10 @@ let
       $out/bin/dmsetup --version 2>&1 | tee -a log | grep -q "version:"
       LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep -q "LVM"
       $out/bin/mdadm --version
+      ${optionalString config.services.multipath.enable ''
+        ($out/bin/multipath || true) 2>&1 | grep -q 'need to be root'
+        ($out/bin/multipathd || true) 2>&1 | grep -q 'need to be root'
+      ''}
 
       ${config.boot.initrd.extraUtilsCommandsTest}
       fi
@@ -338,7 +350,26 @@ let
         { object = pkgs.kmod-debian-aliases;
           symlink = "/etc/modprobe.d/debian.conf";
         }
-      ];
+      ] ++ lib.optionals config.services.multipath.enable [
+        { object = pkgs.runCommand "multipath.conf" {
+              src = config.environment.etc."multipath.conf".text;
+              preferLocalBuild = true;
+            } ''
+              target=$out
+              printf "$src" > $out
+              substituteInPlace $out \
+                --replace ${config.services.multipath.package}/lib ${extraUtils}/lib
+            '';
+          symlink = "/etc/multipath.conf";
+        }
+      ] ++ (lib.mapAttrsToList
+        (symlink: options:
+          {
+            inherit symlink;
+            object = options.source;
+          }
+        )
+        config.boot.initrd.extraFiles);
   };
 
   # Script to add secret files to the initrd at bootloader update time
@@ -411,7 +442,7 @@ in
     boot.initrd.enable = mkOption {
       type = types.bool;
       default = !config.boot.isContainer;
-      defaultText = "!config.boot.isContainer";
+      defaultText = literalExpression "!config.boot.isContainer";
       description = ''
         Whether to enable the NixOS initial RAM disk (initrd). This may be
         needed to perform some initialisation tasks (like mounting
@@ -419,6 +450,22 @@ in
       '';
     };
 
+    boot.initrd.extraFiles = mkOption {
+      default = { };
+      type = types.attrsOf
+        (types.submodule {
+          options = {
+            source = mkOption {
+              type = types.package;
+              description = "The object to make available inside the initrd.";
+            };
+          };
+        });
+      description = ''
+        Extra files to link and copy in to the initrd.
+      '';
+    };
+
     boot.initrd.prepend = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -527,7 +574,7 @@ in
         then "zstd"
         else "gzip"
       );
-      defaultText = "zstd if the kernel supports it (5.9+), gzip if not.";
+      defaultText = literalDocBook "<literal>zstd</literal> if the kernel supports it (5.9+), <literal>gzip</literal> if not";
       type = types.unspecified; # We don't have a function type...
       description = ''
         The compressor to use on the initrd image. May be any of:
@@ -559,7 +606,7 @@ in
             is the path it should be copied from (or null for the same
             path inside and out).
           '';
-        example = literalExample
+        example = literalExpression
           ''
             { "/etc/dropbear/dropbear_rsa_host_key" =
                 ./secret-dropbear-key;
diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix
index f4413188187e..a2b9da2f72e5 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -426,7 +426,7 @@ in
 
     systemd.package = mkOption {
       default = pkgs.systemd;
-      defaultText = "pkgs.systemd";
+      defaultText = literalExpression "pkgs.systemd";
       type = types.package;
       description = "The systemd package.";
     };
@@ -446,7 +446,7 @@ in
     systemd.packages = mkOption {
       default = [];
       type = types.listOf types.package;
-      example = literalExample "[ pkgs.systemd-cryptsetup-generator ]";
+      example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
       description = "Packages providing systemd units and hooks.";
     };
 
@@ -663,7 +663,7 @@ in
 
     services.journald.forwardToSyslog = mkOption {
       default = config.services.rsyslogd.enable || config.services.syslog-ng.enable;
-      defaultText = "services.rsyslogd.enable || services.syslog-ng.enable";
+      defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable";
       type = types.bool;
       description = ''
         Whether to forward log messages to syslog.
@@ -722,7 +722,7 @@ in
 
     services.logind.lidSwitchExternalPower = mkOption {
       default = config.services.logind.lidSwitch;
-      defaultText = "services.logind.lidSwitch";
+      defaultText = literalExpression "services.logind.lidSwitch";
       example = "ignore";
       type = logindHandlerType;
 
@@ -768,7 +768,7 @@ in
     systemd.tmpfiles.packages = mkOption {
       type = types.listOf types.package;
       default = [];
-      example = literalExample "[ pkgs.lvm2 ]";
+      example = literalExpression "[ pkgs.lvm2 ]";
       apply = map getLib;
       description = ''
         List of packages containing <command>systemd-tmpfiles</command> rules.
diff --git a/nixpkgs/nixos/modules/system/etc/etc.nix b/nixpkgs/nixos/modules/system/etc/etc.nix
index 84468ea31f74..8f14f04a1f64 100644
--- a/nixpkgs/nixos/modules/system/etc/etc.nix
+++ b/nixpkgs/nixos/modules/system/etc/etc.nix
@@ -6,9 +6,7 @@ with lib;
 
 let
 
-  # if the source is a local file, it should be imported to the store
-  localToStore = mapAttrs (name: value: if name == "source" then "${value}" else value);
-  etc' = map localToStore (filter (f: f.enable) (attrValues config.environment.etc));
+  etc' = filter (f: f.enable) (attrValues config.environment.etc);
 
   etc = pkgs.runCommandLocal "etc" {
     # This is needed for the systemd module
@@ -55,7 +53,8 @@ let
     mkdir -p "$out/etc"
     ${concatMapStringsSep "\n" (etcEntry: escapeShellArgs [
       "makeEtcEntry"
-      etcEntry.source
+      # Force local source paths to be added to the store
+      "${etcEntry.source}"
       etcEntry.target
       etcEntry.mode
       etcEntry.user
@@ -73,7 +72,7 @@ in
 
     environment.etc = mkOption {
       default = {};
-      example = literalExample ''
+      example = literalExpression ''
         { example-configuration-file =
             { source = "/nix/store/.../etc/dir/file.conf.example";
               mode = "0440";