at 25.11-pre 9.7 kB view raw
1# generate the script used to activate the configuration. 2{ 3 config, 4 lib, 5 pkgs, 6 ... 7}: 8 9with lib; 10 11let 12 13 addAttributeName = mapAttrs ( 14 a: v: 15 v 16 // { 17 text = '' 18 #### Activation script snippet ${a}: 19 _localstatus=0 20 ${v.text} 21 22 if (( _localstatus > 0 )); then 23 printf "Activation script snippet '%s' failed (%s)\n" "${a}" "$_localstatus" 24 fi 25 ''; 26 } 27 ); 28 29 systemActivationScript = 30 set: onlyDry: 31 let 32 set' = mapAttrs ( 33 _: v: if isString v then (noDepEntry v) // { supportsDryActivation = false; } else v 34 ) set; 35 withHeadlines = addAttributeName set'; 36 # When building a dry activation script, this replaces all activation scripts 37 # that do not support dry mode with a comment that does nothing. Filtering these 38 # activation scripts out so they don't get generated into the dry activation script 39 # does not work because when an activation script that supports dry mode depends on 40 # an activation script that does not, the dependency cannot be resolved and the eval 41 # fails. 42 withDrySnippets = mapAttrs ( 43 a: v: 44 if onlyDry && !v.supportsDryActivation then 45 v 46 // { 47 text = "#### Activation script snippet ${a} does not support dry activation."; 48 } 49 else 50 v 51 ) withHeadlines; 52 in 53 '' 54 #!${pkgs.runtimeShell} 55 56 source ${./lib/lib.sh} 57 58 systemConfig='@out@' 59 60 export PATH=/empty 61 for i in ${toString path}; do 62 PATH=$PATH:$i/bin:$i/sbin 63 done 64 65 _status=0 66 trap "_status=1 _localstatus=\$?" ERR 67 68 # Ensure a consistent umask. 69 umask 0022 70 71 ${textClosureMap id (withDrySnippets) (attrNames withDrySnippets)} 72 73 '' 74 + optionalString (!onlyDry) '' 75 # Make this configuration the current configuration. 76 # The readlink is there to ensure that when $systemConfig = /system 77 # (which is a symlink to the store), /run/current-system is still 78 # used as a garbage collection root. 79 ln -sfn "$(readlink -f "$systemConfig")" /run/current-system 80 81 exit $_status 82 ''; 83 84 path = 85 with pkgs; 86 map getBin [ 87 coreutils 88 gnugrep 89 findutils 90 getent 91 stdenv.cc.libc # nscd in update-users-groups.pl 92 shadow 93 nettools # needed for hostname 94 util-linux # needed for mount and mountpoint 95 ]; 96 97 scriptType = 98 withDry: 99 with types; 100 let 101 scriptOptions = 102 { 103 deps = mkOption { 104 type = types.listOf types.str; 105 default = [ ]; 106 description = "List of dependencies. The script will run after these."; 107 }; 108 text = mkOption { 109 type = types.lines; 110 description = "The content of the script."; 111 }; 112 } 113 // optionalAttrs withDry { 114 supportsDryActivation = mkOption { 115 type = types.bool; 116 default = false; 117 description = '' 118 Whether this activation script supports being dry-activated. 119 These activation scripts will also be executed on dry-activate 120 activations with the environment variable 121 `NIXOS_ACTION` being set to `dry-activate`. 122 it's important that these activation scripts don't 123 modify anything about the system when the variable is set. 124 ''; 125 }; 126 }; 127 in 128 either str (submodule { 129 options = scriptOptions; 130 }); 131 132in 133 134{ 135 136 ###### interface 137 138 options = { 139 140 system.activationScripts = mkOption { 141 default = { }; 142 143 example = literalExpression '' 144 { 145 stdio = { 146 # Run after /dev has been mounted 147 deps = [ "specialfs" ]; 148 text = 149 ''' 150 # Needed by some programs. 151 ln -sfn /proc/self/fd /dev/fd 152 ln -sfn /proc/self/fd/0 /dev/stdin 153 ln -sfn /proc/self/fd/1 /dev/stdout 154 ln -sfn /proc/self/fd/2 /dev/stderr 155 '''; 156 }; 157 } 158 ''; 159 160 description = '' 161 A set of shell script fragments that are executed when a NixOS 162 system configuration is activated. Examples are updating 163 /etc, creating accounts, and so on. Since these are executed 164 every time you boot the system or run 165 {command}`nixos-rebuild`, it's important that they are 166 idempotent and fast. 167 ''; 168 169 type = types.attrsOf (scriptType true); 170 apply = 171 set: 172 set 173 // { 174 script = systemActivationScript set false; 175 }; 176 }; 177 178 system.dryActivationScript = mkOption { 179 description = "The shell script that is to be run when dry-activating a system."; 180 readOnly = true; 181 internal = true; 182 default = systemActivationScript (removeAttrs config.system.activationScripts [ "script" ]) true; 183 defaultText = literalMD "generated activation script"; 184 }; 185 186 system.userActivationScripts = mkOption { 187 default = { }; 188 189 example = literalExpression '' 190 { plasmaSetup = { 191 text = ''' 192 ''${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5" 193 '''; 194 deps = []; 195 }; 196 } 197 ''; 198 199 description = '' 200 A set of shell script fragments that are executed by a systemd user 201 service when a NixOS system configuration is activated. Examples are 202 rebuilding the .desktop file cache for showing applications in the menu. 203 Since these are executed every time you run 204 {command}`nixos-rebuild`, it's important that they are 205 idempotent and fast. 206 ''; 207 208 type = with types; attrsOf (scriptType false); 209 210 apply = set: { 211 script = '' 212 export PATH= 213 for i in ${toString path}; do 214 PATH=$PATH:$i/bin:$i/sbin 215 done 216 217 _status=0 218 trap "_status=1 _localstatus=\$?" ERR 219 220 ${ 221 let 222 set' = mapAttrs (n: v: if isString v then noDepEntry v else v) set; 223 withHeadlines = addAttributeName set'; 224 in 225 textClosureMap id (withHeadlines) (attrNames withHeadlines) 226 } 227 228 exit $_status 229 ''; 230 }; 231 232 }; 233 234 environment.usrbinenv = mkOption { 235 default = "${pkgs.coreutils}/bin/env"; 236 defaultText = literalExpression ''"''${pkgs.coreutils}/bin/env"''; 237 example = literalExpression ''"''${pkgs.busybox}/bin/env"''; 238 type = types.nullOr types.path; 239 visible = false; 240 description = '' 241 The {manpage}`env(1)` executable that is linked system-wide to 242 `/usr/bin/env`. 243 ''; 244 }; 245 246 system.build.installBootLoader = mkOption { 247 internal = true; 248 default = pkgs.writeShellScript "no-bootloader" '' 249 echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2 250 ''; 251 defaultText = lib.literalExpression '' 252 pkgs.writeShellScript "no-bootloader" ''' 253 echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2 254 ''' 255 ''; 256 description = '' 257 A program that writes a bootloader installation script to the path passed in the first command line argument. 258 259 See `nixos/modules/system/activation/switch-to-configuration.pl`. 260 ''; 261 type = types.unique { 262 message = '' 263 Only one bootloader can be enabled at a time. This requirement has not 264 been checked until NixOS 22.05. Earlier versions defaulted to the last 265 definition. Change your configuration to enable only one bootloader. 266 ''; 267 } (types.either types.str types.package); 268 }; 269 270 }; 271 272 ###### implementation 273 274 config = { 275 276 system.activationScripts.stdio = ""; # obsolete 277 system.activationScripts.var = ""; # obsolete 278 279 systemd.tmpfiles.rules = 280 [ 281 "D /var/empty 0555 root root -" 282 "h /var/empty - - - - +i" 283 ] 284 ++ lib.optionals config.nix.enable [ 285 # Prevent the current configuration from being garbage-collected. 286 "d /nix/var/nix/gcroots -" 287 "L+ /nix/var/nix/gcroots/current-system - - - - /run/current-system" 288 ]; 289 290 system.activationScripts.usrbinenv = 291 if config.environment.usrbinenv != null then 292 '' 293 mkdir -p /usr/bin 294 chmod 0755 /usr/bin 295 ln -sfn ${config.environment.usrbinenv} /usr/bin/.env.tmp 296 mv /usr/bin/.env.tmp /usr/bin/env # atomically replace /usr/bin/env 297 '' 298 else 299 '' 300 rm -f /usr/bin/env 301 if test -d /usr/bin; then rmdir --ignore-fail-on-non-empty /usr/bin; fi 302 if test -d /usr; then rmdir --ignore-fail-on-non-empty /usr; fi 303 ''; 304 305 system.activationScripts.specialfs = '' 306 specialMount() { 307 local device="$1" 308 local mountPoint="$2" 309 local options="$3" 310 local fsType="$4" 311 312 if mountpoint -q "$mountPoint"; then 313 local options="remount,$options" 314 else 315 mkdir -p "$mountPoint" 316 chmod 0755 "$mountPoint" 317 fi 318 mount -t "$fsType" -o "$options" "$device" "$mountPoint" 319 } 320 source ${config.system.build.earlyMountScript} 321 ''; 322 323 systemd.user = { 324 services.nixos-activation = { 325 description = "Run user-specific NixOS activation"; 326 script = config.system.userActivationScripts.script; 327 unitConfig.ConditionUser = "!@system"; 328 serviceConfig.Type = "oneshot"; 329 wantedBy = [ "default.target" ]; 330 }; 331 }; 332 }; 333 334}