242 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
# generate the script used to activate the configuration.
 | 
						|
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
with lib;
 | 
						|
 | 
						|
let
 | 
						|
 | 
						|
  addAttributeName = mapAttrs (a: v: v // {
 | 
						|
    text = ''
 | 
						|
      #### Activation script snippet ${a}:
 | 
						|
      _localstatus=0
 | 
						|
      ${v.text}
 | 
						|
 | 
						|
      if (( _localstatus > 0 )); then
 | 
						|
        printf "Activation script snippet '%s' failed (%s)\n" "${a}" "$_localstatus"
 | 
						|
      fi
 | 
						|
    '';
 | 
						|
  });
 | 
						|
 | 
						|
  path = with pkgs; map getBin
 | 
						|
    [ coreutils
 | 
						|
      gnugrep
 | 
						|
      findutils
 | 
						|
      getent
 | 
						|
      stdenv.cc.libc # nscd in update-users-groups.pl
 | 
						|
      shadow
 | 
						|
      nettools # needed for hostname
 | 
						|
      util-linux # needed for mount and mountpoint
 | 
						|
    ];
 | 
						|
 | 
						|
  scriptType = with types;
 | 
						|
    let scriptOptions =
 | 
						|
      { deps = mkOption
 | 
						|
          { type = types.listOf types.str;
 | 
						|
            default = [ ];
 | 
						|
            description = "List of dependencies. The script will run after these.";
 | 
						|
          };
 | 
						|
        text = mkOption
 | 
						|
          { type = types.lines;
 | 
						|
            description = "The content of the script.";
 | 
						|
          };
 | 
						|
      };
 | 
						|
    in either str (submodule { options = scriptOptions; });
 | 
						|
 | 
						|
in
 | 
						|
 | 
						|
{
 | 
						|
 | 
						|
  ###### interface
 | 
						|
 | 
						|
  options = {
 | 
						|
 | 
						|
    system.activationScripts = mkOption {
 | 
						|
      default = {};
 | 
						|
 | 
						|
      example = literalExample ''
 | 
						|
        { stdio.text =
 | 
						|
          '''
 | 
						|
            # Needed by some programs.
 | 
						|
            ln -sfn /proc/self/fd /dev/fd
 | 
						|
            ln -sfn /proc/self/fd/0 /dev/stdin
 | 
						|
            ln -sfn /proc/self/fd/1 /dev/stdout
 | 
						|
            ln -sfn /proc/self/fd/2 /dev/stderr
 | 
						|
          ''';
 | 
						|
        }
 | 
						|
      '';
 | 
						|
 | 
						|
      description = ''
 | 
						|
        A set of shell script fragments that are executed when a NixOS
 | 
						|
        system configuration is activated.  Examples are updating
 | 
						|
        /etc, creating accounts, and so on.  Since these are executed
 | 
						|
        every time you boot the system or run
 | 
						|
        <command>nixos-rebuild</command>, it's important that they are
 | 
						|
        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
 | 
						|
          '';
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
    system.userActivationScripts = mkOption {
 | 
						|
      default = {};
 | 
						|
 | 
						|
      example = literalExample ''
 | 
						|
        { plasmaSetup = {
 | 
						|
            text = '''
 | 
						|
              ${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5"
 | 
						|
            ''';
 | 
						|
            deps = [];
 | 
						|
          };
 | 
						|
        }
 | 
						|
      '';
 | 
						|
 | 
						|
      description = ''
 | 
						|
        A set of shell script fragments that are executed by a systemd user
 | 
						|
        service when a NixOS system configuration is activated. Examples are
 | 
						|
        rebuilding the .desktop file cache for showing applications in the menu.
 | 
						|
        Since these are executed every time you run
 | 
						|
        <command>nixos-rebuild</command>, it's important that they are
 | 
						|
        idempotent and fast.
 | 
						|
      '';
 | 
						|
 | 
						|
      type = with types; attrsOf scriptType;
 | 
						|
 | 
						|
      apply = set: {
 | 
						|
        script = ''
 | 
						|
          unset PATH
 | 
						|
          for i in ${toString path}; do
 | 
						|
            PATH=$PATH:$i/bin:$i/sbin
 | 
						|
          done
 | 
						|
 | 
						|
          _status=0
 | 
						|
          trap "_status=1 _localstatus=\$?" ERR
 | 
						|
 | 
						|
          ${
 | 
						|
            let
 | 
						|
              set' = mapAttrs (n: v: if isString v then noDepEntry v else v) set;
 | 
						|
              withHeadlines = addAttributeName set';
 | 
						|
            in textClosureMap id (withHeadlines) (attrNames withHeadlines)
 | 
						|
          }
 | 
						|
 | 
						|
          exit $_status
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
    };
 | 
						|
 | 
						|
    environment.usrbinenv = mkOption {
 | 
						|
      default = "${pkgs.coreutils}/bin/env";
 | 
						|
      example = literalExample ''
 | 
						|
        "''${pkgs.busybox}/bin/env"
 | 
						|
      '';
 | 
						|
      type = types.nullOr types.path;
 | 
						|
      visible = false;
 | 
						|
      description = ''
 | 
						|
        The env(1) executable that is linked system-wide to
 | 
						|
        <literal>/usr/bin/env</literal>.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  ###### implementation
 | 
						|
 | 
						|
  config = {
 | 
						|
 | 
						|
    system.activationScripts.stdio = ""; # obsolete
 | 
						|
 | 
						|
    system.activationScripts.var =
 | 
						|
      ''
 | 
						|
        # Various log/runtime directories.
 | 
						|
 | 
						|
        mkdir -m 1777 -p /var/tmp
 | 
						|
 | 
						|
        # Empty, immutable home directory of many system accounts.
 | 
						|
        mkdir -p /var/empty
 | 
						|
        # Make sure it's really empty
 | 
						|
        ${pkgs.e2fsprogs}/bin/chattr -f -i /var/empty || true
 | 
						|
        find /var/empty -mindepth 1 -delete
 | 
						|
        chmod 0555 /var/empty
 | 
						|
        chown root:root /var/empty
 | 
						|
        ${pkgs.e2fsprogs}/bin/chattr -f +i /var/empty || true
 | 
						|
      '';
 | 
						|
 | 
						|
    system.activationScripts.usrbinenv = if config.environment.usrbinenv != null
 | 
						|
      then ''
 | 
						|
        mkdir -m 0755 -p /usr/bin
 | 
						|
        ln -sfn ${config.environment.usrbinenv} /usr/bin/.env.tmp
 | 
						|
        mv /usr/bin/.env.tmp /usr/bin/env # atomically replace /usr/bin/env
 | 
						|
      ''
 | 
						|
      else ''
 | 
						|
        rm -f /usr/bin/env
 | 
						|
        rmdir --ignore-fail-on-non-empty /usr/bin /usr
 | 
						|
      '';
 | 
						|
 | 
						|
    system.activationScripts.specialfs =
 | 
						|
      ''
 | 
						|
        specialMount() {
 | 
						|
          local device="$1"
 | 
						|
          local mountPoint="$2"
 | 
						|
          local options="$3"
 | 
						|
          local fsType="$4"
 | 
						|
 | 
						|
          if mountpoint -q "$mountPoint"; then
 | 
						|
            local options="remount,$options"
 | 
						|
          else
 | 
						|
            mkdir -m 0755 -p "$mountPoint"
 | 
						|
          fi
 | 
						|
          mount -t "$fsType" -o "$options" "$device" "$mountPoint"
 | 
						|
        }
 | 
						|
        source ${config.system.build.earlyMountScript}
 | 
						|
      '';
 | 
						|
 | 
						|
    systemd.user = {
 | 
						|
      services.nixos-activation = {
 | 
						|
        description = "Run user-specific NixOS activation";
 | 
						|
        script = config.system.userActivationScripts.script;
 | 
						|
        unitConfig.ConditionUser = "!@system";
 | 
						|
        serviceConfig.Type = "oneshot";
 | 
						|
      };
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
}
 |