at master 9.6 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 util-linux # needed for mount and mountpoint 94 ]; 95 96 scriptType = 97 withDry: 98 with types; 99 let 100 scriptOptions = { 101 deps = mkOption { 102 type = types.listOf types.str; 103 default = [ ]; 104 description = "List of dependencies. The script will run after these."; 105 }; 106 text = mkOption { 107 type = types.lines; 108 description = "The content of the script."; 109 }; 110 } 111 // optionalAttrs withDry { 112 supportsDryActivation = mkOption { 113 type = types.bool; 114 default = false; 115 description = '' 116 Whether this activation script supports being dry-activated. 117 These activation scripts will also be executed on dry-activate 118 activations with the environment variable 119 `NIXOS_ACTION` being set to `dry-activate`. 120 it's important that these activation scripts don't 121 modify anything about the system when the variable is set. 122 ''; 123 }; 124 }; 125 in 126 either str (submodule { 127 options = scriptOptions; 128 }); 129 130in 131 132{ 133 134 ###### interface 135 136 options = { 137 138 system.activationScripts = mkOption { 139 default = { }; 140 141 example = literalExpression '' 142 { 143 stdio = { 144 # Run after /dev has been mounted 145 deps = [ "specialfs" ]; 146 text = 147 ''' 148 # Needed by some programs. 149 ln -sfn /proc/self/fd /dev/fd 150 ln -sfn /proc/self/fd/0 /dev/stdin 151 ln -sfn /proc/self/fd/1 /dev/stdout 152 ln -sfn /proc/self/fd/2 /dev/stderr 153 '''; 154 }; 155 } 156 ''; 157 158 description = '' 159 A set of shell script fragments that are executed when a NixOS 160 system configuration is activated. Examples are updating 161 /etc, creating accounts, and so on. Since these are executed 162 every time you boot the system or run 163 {command}`nixos-rebuild`, it's important that they are 164 idempotent and fast. 165 ''; 166 167 type = types.attrsOf (scriptType true); 168 apply = 169 set: 170 set 171 // { 172 script = systemActivationScript set false; 173 }; 174 }; 175 176 system.dryActivationScript = mkOption { 177 description = "The shell script that is to be run when dry-activating a system."; 178 readOnly = true; 179 internal = true; 180 default = systemActivationScript (removeAttrs config.system.activationScripts [ "script" ]) true; 181 defaultText = literalMD "generated activation script"; 182 }; 183 184 system.userActivationScripts = mkOption { 185 default = { }; 186 187 example = literalExpression '' 188 { plasmaSetup = { 189 text = ''' 190 ''${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5" 191 '''; 192 deps = []; 193 }; 194 } 195 ''; 196 197 description = '' 198 A set of shell script fragments that are executed by a systemd user 199 service when a NixOS system configuration is activated. Examples are 200 rebuilding the .desktop file cache for showing applications in the menu. 201 Since these are executed every time you run 202 {command}`nixos-rebuild`, it's important that they are 203 idempotent and fast. 204 ''; 205 206 type = with types; attrsOf (scriptType false); 207 208 apply = set: { 209 script = '' 210 export PATH= 211 for i in ${toString path}; do 212 PATH=$PATH:$i/bin:$i/sbin 213 done 214 215 _status=0 216 trap "_status=1 _localstatus=\$?" ERR 217 218 ${ 219 let 220 set' = mapAttrs (n: v: if isString v then noDepEntry v else v) set; 221 withHeadlines = addAttributeName set'; 222 in 223 textClosureMap id (withHeadlines) (attrNames withHeadlines) 224 } 225 226 exit $_status 227 ''; 228 }; 229 230 }; 231 232 environment.usrbinenv = mkOption { 233 default = "${pkgs.coreutils}/bin/env"; 234 defaultText = literalExpression ''"''${pkgs.coreutils}/bin/env"''; 235 example = literalExpression ''"''${pkgs.busybox}/bin/env"''; 236 type = types.nullOr types.path; 237 visible = false; 238 description = '' 239 The {manpage}`env(1)` executable that is linked system-wide to 240 `/usr/bin/env`. 241 ''; 242 }; 243 244 system.build.installBootLoader = mkOption { 245 internal = true; 246 default = pkgs.writeShellScript "no-bootloader" '' 247 echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2 248 ''; 249 defaultText = lib.literalExpression '' 250 pkgs.writeShellScript "no-bootloader" ''' 251 echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2 252 ''' 253 ''; 254 description = '' 255 A program that writes a bootloader installation script to the path passed in the first command line argument. 256 257 See `pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs`. 258 ''; 259 type = types.unique { 260 message = '' 261 Only one bootloader can be enabled at a time. This requirement has not 262 been checked until NixOS 22.05. Earlier versions defaulted to the last 263 definition. Change your configuration to enable only one bootloader. 264 ''; 265 } (types.either types.str types.package); 266 }; 267 268 }; 269 270 ###### implementation 271 272 config = { 273 274 system.activationScripts.stdio = ""; # obsolete 275 system.activationScripts.var = ""; # obsolete 276 277 systemd.tmpfiles.rules = [ 278 "D /var/empty 0555 root root -" 279 "h /var/empty - - - - +i" 280 ] 281 ++ lib.optionals config.nix.enable [ 282 # Prevent the current configuration from being garbage-collected. 283 "d /nix/var/nix/gcroots -" 284 "L+ /nix/var/nix/gcroots/current-system - - - - /run/current-system" 285 ]; 286 287 system.activationScripts.usrbinenv = 288 if config.environment.usrbinenv != null then 289 '' 290 mkdir -p /usr/bin 291 chmod 0755 /usr/bin 292 ln -sfn ${config.environment.usrbinenv} /usr/bin/.env.tmp 293 mv /usr/bin/.env.tmp /usr/bin/env # atomically replace /usr/bin/env 294 '' 295 else 296 '' 297 rm -f /usr/bin/env 298 if test -d /usr/bin; then rmdir --ignore-fail-on-non-empty /usr/bin; fi 299 if test -d /usr; then rmdir --ignore-fail-on-non-empty /usr; fi 300 ''; 301 302 system.activationScripts.specialfs = '' 303 specialMount() { 304 local device="$1" 305 local mountPoint="$2" 306 local options="$3" 307 local fsType="$4" 308 309 if mountpoint -q "$mountPoint"; then 310 local options="remount,$options" 311 else 312 mkdir -p "$mountPoint" 313 chmod 0755 "$mountPoint" 314 fi 315 mount -t "$fsType" -o "$options" "$device" "$mountPoint" 316 } 317 source ${config.system.build.earlyMountScript} 318 ''; 319 320 systemd.user = lib.mkIf config.system.activatable { 321 services.nixos-activation = { 322 description = "Run user-specific NixOS activation"; 323 script = config.system.userActivationScripts.script; 324 unitConfig.ConditionUser = "!@system"; 325 serviceConfig.Type = "oneshot"; 326 wantedBy = [ "default.target" ]; 327 }; 328 }; 329 }; 330 331}