diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 3a6930314b1a..548b4de852b7 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -17,6 +17,41 @@ let ''; }); + systemActivationScript = set: onlyDry: let + set' = filterAttrs (_: v: onlyDry -> v.supportsDryActivation) (mapAttrs (_: v: if isString v then (noDepEntry v) // { supportsDryActivation = false; } else v) set); + withHeadlines = addAttributeName set'; + in + '' + #!${pkgs.runtimeShell} + + systemConfig='@out@' + + export PATH=/empty + for i in ${toString path}; do + PATH=$PATH:$i/bin:$i/sbin + done + + _status=0 + trap "_status=1 _localstatus=\$?" ERR + + # Ensure a consistent umask. + umask 0022 + + ${textClosureMap id (withHeadlines) (attrNames withHeadlines)} + + '' + optionalString (!onlyDry) '' + # Make this configuration the current configuration. + # The readlink is there to ensure that when $systemConfig = /system + # (which is a symlink to the store), /run/current-system is still + # used as a garbage collection root. + ln -sfn "$(readlink -f "$systemConfig")" /run/current-system + + # Prevent the current configuration from being garbage-collected. + ln -sfn /run/current-system /nix/var/nix/gcroots/current-system + + exit $_status + ''; + path = with pkgs; map getBin [ coreutils gnugrep @@ -28,7 +63,7 @@ let util-linux # needed for mount and mountpoint ]; - scriptType = with types; + scriptType = withDry: with types; let scriptOptions = { deps = mkOption { type = types.listOf types.str; @@ -39,6 +74,19 @@ let { type = types.lines; description = "The content of the script."; }; + } // optionalAttrs withDry { + supportsDryActivation = mkOption + { type = types.bool; + default = false; + description = '' + Whether this activation script supports being dry-activated. + These activation scripts will also be executed on dry-activate + activations with the environment variable + NIXOS_ACTION being set to dry-activate + . it's important that these activation scripts don't + modify anything about the system when the variable is set. + ''; + }; }; in either str (submodule { options = scriptOptions; }); @@ -74,47 +122,19 @@ in idempotent and fast. ''; - type = types.attrsOf scriptType; - - apply = set: { - script = - '' - #! ${pkgs.runtimeShell} - - systemConfig=@out@ - - export PATH=/empty - for i in ${toString path}; do - PATH=$PATH:$i/bin:$i/sbin - done - - _status=0 - trap "_status=1 _localstatus=\$?" ERR - - # Ensure a consistent umask. - umask 0022 - - ${ - let - set' = mapAttrs (n: v: if isString v then noDepEntry v else v) set; - withHeadlines = addAttributeName set'; - in textClosureMap id (withHeadlines) (attrNames withHeadlines) - } - - # Make this configuration the current configuration. - # The readlink is there to ensure that when $systemConfig = /system - # (which is a symlink to the store), /run/current-system is still - # used as a garbage collection root. - ln -sfn "$(readlink -f "$systemConfig")" /run/current-system - - # Prevent the current configuration from being garbage-collected. - ln -sfn /run/current-system /nix/var/nix/gcroots/current-system - - exit $_status - ''; + type = types.attrsOf (scriptType true); + apply = set: set // { + script = systemActivationScript set false; }; }; + system.dryActivationScript = mkOption { + description = "The shell script that is to be run when dry-activating a system."; + readOnly = true; + internal = true; + default = systemActivationScript (removeAttrs config.system.activationScripts [ "script" ]) true; + }; + system.userActivationScripts = mkOption { default = {}; @@ -137,7 +157,7 @@ in idempotent and fast. ''; - type = with types; attrsOf scriptType; + type = with types; attrsOf (scriptType false); apply = set: { script = '' diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index dd391c8b5d78..b7a062755296 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -36,6 +36,8 @@ EOF exit 1; } +$ENV{NIXOS_ACTION} = $action; + # This is a NixOS installation if it has /etc/NIXOS or a proper # /etc/os-release. die "This is not a NixOS installation!\n" unless @@ -360,6 +362,10 @@ if ($action eq "dry-activate") { if scalar @unitsToStopFiltered > 0; print STDERR "would NOT stop the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n" if scalar(keys %unitsToSkip) > 0; + + print STDERR "would activate the configuration...\n"; + system("$out/dry-activate", "$out"); + print STDERR "would restart systemd\n" if $restartSystemd; print STDERR "would restart the following units: ", join(", ", sort(keys %unitsToRestart)), "\n" if scalar(keys %unitsToRestart) > 0; diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index d3e4923a993f..80835d9688f2 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -56,9 +56,11 @@ let ''} echo "$activationScript" > $out/activate + echo "$dryActivationScript" > $out/dry-activate substituteInPlace $out/activate --subst-var out - chmod u+x $out/activate - unset activationScript + substituteInPlace $out/dry-activate --subst-var out + chmod u+x $out/activate $out/dry-activate + unset activationScript dryActivationScript cp ${config.system.build.bootStage2} $out/init substituteInPlace $out/init --subst-var-by systemConfig $out @@ -108,6 +110,7 @@ let config.system.build.installBootLoader or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; activationScript = config.system.activationScripts.script; + dryActivationScript = config.system.dryActivationScript; nixosLabel = config.system.nixos.label; configurationName = config.boot.loader.grub.configurationName;