summary refs log tree commit diff
path: root/nixos/modules/installer/tools
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/installer/tools')
-rw-r--r--nixos/modules/installer/tools/get-version-suffix22
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix7
-rw-r--r--nixos/modules/installer/tools/nixos-build-vms/build-vms.nix31
-rw-r--r--nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh53
-rw-r--r--nixos/modules/installer/tools/nixos-enter.sh109
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl675
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh218
-rw-r--r--nixos/modules/installer/tools/nixos-option/default.nix1
-rw-r--r--nixos/modules/installer/tools/nixos-version.sh24
-rw-r--r--nixos/modules/installer/tools/tools.nix235
10 files changed, 1375 insertions, 0 deletions
diff --git a/nixos/modules/installer/tools/get-version-suffix b/nixos/modules/installer/tools/get-version-suffix
new file mode 100644
index 00000000000..b8972cd57d2
--- /dev/null
+++ b/nixos/modules/installer/tools/get-version-suffix
@@ -0,0 +1,22 @@
+getVersion() {
+    local dir="$1"
+    rev=
+    if [ -e "$dir/.git" ]; then
+        if [ -z "$(type -P git)" ]; then
+            echo "warning: Git not found; cannot figure out revision of $dir" >&2
+            return
+        fi
+        cd "$dir"
+        rev=$(git rev-parse --short HEAD)
+        if git describe --always --dirty | grep -q dirty; then
+            rev+=M
+        fi
+    fi
+}
+
+if nixpkgs=$(nix-instantiate --find-file nixpkgs "$@"); then
+    getVersion $nixpkgs
+    if [ -n "$rev" ]; then
+        echo ".git.$rev"
+    fi
+fi
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
new file mode 100644
index 00000000000..dfafda77cb5
--- /dev/null
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -0,0 +1,7 @@
+{
+  x86_64-linux = "/nix/store/0n2wfvi1i3fg97cjc54wslvk0804y0sn-nix-2.7.0";
+  i686-linux = "/nix/store/4p27c1k9z99pli6x8cxfph20yfyzn9nh-nix-2.7.0";
+  aarch64-linux = "/nix/store/r9yr8ijsb0gi9r7y92y3yzyld59yp0kj-nix-2.7.0";
+  x86_64-darwin = "/nix/store/hyfj5imsd0c4amlcjpf8l6w4q2draaj3-nix-2.7.0";
+  aarch64-darwin = "/nix/store/9l96qllhbb6xrsjaai76dn74ap7rq92n-nix-2.7.0";
+}
diff --git a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
new file mode 100644
index 00000000000..b4a94f62ad9
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
@@ -0,0 +1,31 @@
+{ system ? builtins.currentSystem
+, config ? {}
+, networkExpr
+}:
+
+let
+  nodes = builtins.mapAttrs (vm: module: {
+    _file = "${networkExpr}@node-${vm}";
+    imports = [ module ];
+  }) (import networkExpr);
+
+  pkgs = import ../../../../.. { inherit system config; };
+
+  testing = import ../../../../lib/testing-python.nix {
+    inherit system pkgs;
+  };
+
+  interactiveDriver = (testing.makeTest { inherit nodes; testScript = "start_all(); join_all();"; }).driverInteractive;
+in
+
+
+pkgs.runCommand "nixos-build-vms" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
+  mkdir -p $out/bin
+  ln -s ${interactiveDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
+  ln -s ${interactiveDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
+  wrapProgram $out/bin/nixos-test-driver \
+    --add-flags "--interactive"
+  wrapProgram $out/bin/nixos-run-vms \
+     --set testScript "${pkgs.writeText "start-all" "start_all(); join_all();"}" \
+     --add-flags "--no-interactive"
+''
diff --git a/nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh b/nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh
new file mode 100644
index 00000000000..490ede04e6b
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh
@@ -0,0 +1,53 @@
+#! @runtimeShell@ -e
+# shellcheck shell=bash
+
+# Shows the usage of this command to the user
+
+showUsage() {
+    exec man nixos-build-vms
+    exit 1
+}
+
+# Parse valid argument options
+
+nixBuildArgs=()
+networkExpr=
+
+while [ $# -gt 0 ]; do
+    case "$1" in
+      --no-out-link)
+        nixBuildArgs+=("--no-out-link")
+        ;;
+      --show-trace)
+        nixBuildArgs+=("--show-trace")
+        ;;
+      -h|--help)
+        showUsage
+        exit 0
+        ;;
+      --option)
+        shift
+        nixBuildArgs+=("--option" "$1" "$2"); shift
+        ;;
+      *)
+        if [ -n "$networkExpr" ]; then
+          echo "Network expression already set!"
+          showUsage
+          exit 1
+        fi
+        networkExpr="$(readlink -f "$1")"
+        ;;
+    esac
+
+    shift
+done
+
+if [ -z "$networkExpr" ]
+then
+    echo "ERROR: A network expression must be specified!" >&2
+    exit 1
+fi
+
+# Build a network of VMs
+nix-build '<nixpkgs/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix>' \
+    --argstr networkExpr "$networkExpr" "${nixBuildArgs[@]}"
diff --git a/nixos/modules/installer/tools/nixos-enter.sh b/nixos/modules/installer/tools/nixos-enter.sh
new file mode 100644
index 00000000000..89beeee7cf9
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-enter.sh
@@ -0,0 +1,109 @@
+#! @runtimeShell@
+# shellcheck shell=bash
+
+set -e
+
+# Re-exec ourselves in a private mount namespace so that our bind
+# mounts get cleaned up automatically.
+if [ -z "$NIXOS_ENTER_REEXEC" ]; then
+    export NIXOS_ENTER_REEXEC=1
+    if [ "$(id -u)" != 0 ]; then
+        extraFlags="-r"
+    fi
+    exec unshare --fork --mount --uts --mount-proc --pid $extraFlags -- "$0" "$@"
+else
+    mount --make-rprivate /
+fi
+
+mountPoint=/mnt
+system=/nix/var/nix/profiles/system
+command=("$system/sw/bin/bash" "--login")
+silent=0
+
+while [ "$#" -gt 0 ]; do
+    i="$1"; shift 1
+    case "$i" in
+        --root)
+            mountPoint="$1"; shift 1
+            ;;
+        --system)
+            system="$1"; shift 1
+            ;;
+        --help)
+            exec man nixos-enter
+            exit 1
+            ;;
+        --command|-c)
+            command=("$system/sw/bin/bash" "-c" "$1")
+            shift 1
+            ;;
+        --silent)
+            silent=1
+            ;;
+        --)
+            command=("$@")
+            break
+            ;;
+        *)
+            echo "$0: unknown option \`$i'"
+            exit 1
+            ;;
+    esac
+done
+
+if [[ ! -e $mountPoint/etc/NIXOS ]]; then
+    echo "$0: '$mountPoint' is not a NixOS installation" >&2
+    exit 126
+fi
+
+mkdir -p "$mountPoint/dev" "$mountPoint/sys"
+chmod 0755 "$mountPoint/dev" "$mountPoint/sys"
+mount --rbind /dev "$mountPoint/dev"
+mount --rbind /sys "$mountPoint/sys"
+
+# modified from https://github.com/archlinux/arch-install-scripts/blob/bb04ab435a5a89cd5e5ee821783477bc80db797f/arch-chroot.in#L26-L52
+chroot_add_resolv_conf() {
+    local chrootDir="$1" resolvConf="$1/etc/resolv.conf"
+
+    [[ -e /etc/resolv.conf ]] || return 0
+
+    # Handle resolv.conf as a symlink to somewhere else.
+    if [[ -L "$resolvConf" ]]; then
+      # readlink(1) should always give us *something* since we know at this point
+      # it's a symlink. For simplicity, ignore the case of nested symlinks.
+      # We also ignore the possibility of `../`s escaping the root.
+      resolvConf="$(readlink "$resolvConf")"
+      if [[ "$resolvConf" = /* ]]; then
+        resolvConf="$chrootDir$resolvConf"
+      else
+        resolvConf="$chrootDir/etc/$resolvConf"
+      fi
+    fi
+
+    # ensure file exists to bind mount over
+    if [[ ! -f "$resolvConf" ]]; then
+      install -Dm644 /dev/null "$resolvConf" || return 1
+    fi
+
+    mount --bind /etc/resolv.conf "$resolvConf"
+}
+
+chroot_add_resolv_conf "$mountPoint" || echo "$0: failed to set up resolv.conf" >&2
+
+(
+    # If silent, write both stdout and stderr of activation script to /dev/null
+    # otherwise, write both streams to stderr of this process
+    if [ "$silent" -eq 1 ]; then
+        exec 2>/dev/null
+    fi
+
+    # Run the activation script. Set $LOCALE_ARCHIVE to supress some Perl locale warnings.
+    LOCALE_ARCHIVE="$system/sw/lib/locale/locale-archive" IN_NIXOS_ENTER=1 chroot "$mountPoint" "$system/activate" 1>&2 || true
+
+    # Create /tmp
+    chroot "$mountPoint" systemd-tmpfiles --create --remove --exclude-prefix=/dev 1>&2 || true
+)
+
+unset TMPDIR
+
+exec chroot "$mountPoint" "${command[@]}"
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
new file mode 100644
index 00000000000..57aef50a0f6
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -0,0 +1,675 @@
+#! @perl@
+
+use strict;
+use Cwd 'abs_path';
+use File::Spec;
+use File::Path;
+use File::Basename;
+use File::Slurp;
+use File::stat;
+
+umask(0022);
+
+sub uniq {
+    my %seen;
+    my @res = ();
+    foreach my $s (@_) {
+        if (!defined $seen{$s}) {
+            $seen{$s} = 1;
+            push @res, $s;
+        }
+    }
+    return @res;
+}
+
+sub runCommand {
+    my ($cmd) = @_;
+    open FILE, "$cmd 2>&1 |" or die "Failed to execute: $cmd\n";
+    my @ret = <FILE>;
+    close FILE;
+    return ($?, @ret);
+}
+
+# Process the command line.
+my $outDir = "/etc/nixos";
+my $rootDir = ""; # = /
+my $force = 0;
+my $noFilesystems = 0;
+my $showHardwareConfig = 0;
+
+for (my $n = 0; $n < scalar @ARGV; $n++) {
+    my $arg = $ARGV[$n];
+    if ($arg eq "--help") {
+        exec "man nixos-generate-config" or die;
+    }
+    elsif ($arg eq "--dir") {
+        $n++;
+        $outDir = $ARGV[$n];
+        die "$0: ‘--dir’ requires an argument\n" unless defined $outDir;
+    }
+    elsif ($arg eq "--root") {
+        $n++;
+        $rootDir = $ARGV[$n];
+        die "$0: ‘--root’ requires an argument\n" unless defined $rootDir;
+        $rootDir =~ s/\/*$//; # remove trailing slashes
+    }
+    elsif ($arg eq "--force") {
+        $force = 1;
+    }
+    elsif ($arg eq "--no-filesystems") {
+        $noFilesystems = 1;
+    }
+    elsif ($arg eq "--show-hardware-config") {
+        $showHardwareConfig = 1;
+    }
+    else {
+        die "$0: unrecognized argument ‘$arg’\n";
+    }
+}
+
+
+my @attrs = ();
+my @kernelModules = ();
+my @initrdKernelModules = ();
+my @initrdAvailableKernelModules = ();
+my @modulePackages = ();
+my @imports;
+
+
+sub debug {
+    return unless defined $ENV{"DEBUG"};
+    print STDERR @_;
+}
+
+
+my $cpuinfo = read_file "/proc/cpuinfo";
+
+
+sub hasCPUFeature {
+    my $feature = shift;
+    return $cpuinfo =~ /^flags\s*:.* $feature( |$)/m;
+}
+
+
+sub cpuManufacturer {
+    my $id = shift;
+    return $cpuinfo =~ /^vendor_id\s*:.* $id$/m;
+}
+
+
+# Determine CPU governor to use
+if (-e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors") {
+    my $governors = read_file("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors");
+    # ondemand governor is not available on sandy bridge or later Intel CPUs
+    my @desired_governors = ("ondemand", "powersave");
+    my $e;
+
+    foreach $e (@desired_governors) {
+        if (index($governors, $e) != -1) {
+            last if (push @attrs, "powerManagement.cpuFreqGovernor = lib.mkDefault \"$e\";");
+        }
+    }
+}
+
+
+# Virtualization support?
+push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
+push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
+
+push @attrs, "hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "AuthenticAMD";
+push @attrs, "hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "GenuineIntel";
+
+
+# Look at the PCI devices and add necessary modules.  Note that most
+# modules are auto-detected so we don't need to list them here.
+# However, some are needed in the initrd to boot the system.
+
+my $videoDriver;
+
+sub pciCheck {
+    my $path = shift;
+    my $vendor = read_file "$path/vendor"; chomp $vendor;
+    my $device = read_file "$path/device"; chomp $device;
+    my $class = read_file "$path/class"; chomp $class;
+
+    my $module;
+    if (-e "$path/driver/module") {
+        $module = basename `readlink -f $path/driver/module`;
+        chomp $module;
+    }
+
+    debug "$path: $vendor $device $class";
+    debug " $module" if defined $module;
+    debug "\n";
+
+    if (defined $module) {
+        # See the bottom of http://pciids.sourceforge.net/pci.ids for
+        # device classes.
+        if (# Mass-storage controller.  Definitely important.
+            $class =~ /^0x01/ ||
+
+            # Firewire controller.  A disk might be attached.
+            $class =~ /^0x0c00/ ||
+
+            # USB controller.  Needed if we want to use the
+            # keyboard when things go wrong in the initrd.
+            $class =~ /^0x0c03/
+            )
+        {
+            push @initrdAvailableKernelModules, $module;
+        }
+    }
+
+    # broadcom STA driver (wl.ko)
+    # list taken from http://www.broadcom.com/docs/linux_sta/README.txt
+    if ($vendor eq "0x14e4" &&
+        ($device eq "0x4311" || $device eq "0x4312" || $device eq "0x4313" ||
+         $device eq "0x4315" || $device eq "0x4327" || $device eq "0x4328" ||
+         $device eq "0x4329" || $device eq "0x432a" || $device eq "0x432b" ||
+         $device eq "0x432c" || $device eq "0x432d" || $device eq "0x4353" ||
+         $device eq "0x4357" || $device eq "0x4358" || $device eq "0x4359" ||
+         $device eq "0x4331" || $device eq "0x43a0" || $device eq "0x43b1"
+        ) )
+     {
+        push @modulePackages, "config.boot.kernelPackages.broadcom_sta";
+        push @kernelModules, "wl";
+     }
+
+    # broadcom FullMac driver
+    # list taken from
+    # https://wireless.wiki.kernel.org/en/users/Drivers/brcm80211#brcmfmac
+    if ($vendor eq "0x14e4" &&
+        ($device eq "0x43a3" || $device eq "0x43df" || $device eq "0x43ec" ||
+         $device eq "0x43d3" || $device eq "0x43d9" || $device eq "0x43e9" ||
+         $device eq "0x43ba" || $device eq "0x43bb" || $device eq "0x43bc" ||
+         $device eq "0xaa52" || $device eq "0x43ca" || $device eq "0x43cb" ||
+         $device eq "0x43cc" || $device eq "0x43c3" || $device eq "0x43c4" ||
+         $device eq "0x43c5"
+        ) )
+    {
+        # we need e.g. brcmfmac43602-pcie.bin
+        push @imports, "(modulesPath + \"/hardware/network/broadcom-43xx.nix\")";
+    }
+
+    # In case this is a virtio scsi device, we need to explicitly make this available.
+    if ($vendor eq "0x1af4" && $device eq "0x1004") {
+        push @initrdAvailableKernelModules, "virtio_scsi";
+    }
+
+    # Can't rely on $module here, since the module may not be loaded
+    # due to missing firmware.  Ideally we would check modules.pcimap
+    # here.
+    push @attrs, "networking.enableIntel2200BGFirmware = true;" if
+        $vendor eq "0x8086" &&
+        ($device eq "0x1043" || $device eq "0x104f" || $device eq "0x4220" ||
+         $device eq "0x4221" || $device eq "0x4223" || $device eq "0x4224");
+
+    push @attrs, "networking.enableIntel3945ABGFirmware = true;" if
+        $vendor eq "0x8086" &&
+        ($device eq "0x4229" || $device eq "0x4230" ||
+         $device eq "0x4222" || $device eq "0x4227");
+
+    # Assume that all NVIDIA cards are supported by the NVIDIA driver.
+    # There may be exceptions (e.g. old cards).
+    # FIXME: do we want to enable an unfree driver here?
+    #$videoDriver = "nvidia" if $vendor eq "0x10de" && $class =~ /^0x03/;
+}
+
+foreach my $path (glob "/sys/bus/pci/devices/*") {
+    pciCheck $path;
+}
+
+# Idem for USB devices.
+
+sub usbCheck {
+    my $path = shift;
+    my $class = read_file "$path/bInterfaceClass"; chomp $class;
+    my $subclass = read_file "$path/bInterfaceSubClass"; chomp $subclass;
+    my $protocol = read_file "$path/bInterfaceProtocol"; chomp $protocol;
+
+    my $module;
+    if (-e "$path/driver/module") {
+        $module = basename `readlink -f $path/driver/module`;
+        chomp $module;
+    }
+
+    debug "$path: $class $subclass $protocol";
+    debug " $module" if defined $module;
+    debug "\n";
+
+    if (defined $module) {
+        if (# Mass-storage controller.  Definitely important.
+            $class eq "08" ||
+
+            # Keyboard.  Needed if we want to use the
+            # keyboard when things go wrong in the initrd.
+            ($class eq "03" && $protocol eq "01")
+            )
+        {
+            push @initrdAvailableKernelModules, $module;
+        }
+    }
+}
+
+foreach my $path (glob "/sys/bus/usb/devices/*") {
+    if (-e "$path/bInterfaceClass") {
+        usbCheck $path;
+    }
+}
+
+
+# Add the modules for all block and MMC devices.
+foreach my $path (glob "/sys/class/{block,mmc_host}/*") {
+    my $module;
+    if (-e "$path/device/driver/module") {
+        $module = basename `readlink -f $path/device/driver/module`;
+        chomp $module;
+        push @initrdAvailableKernelModules, $module;
+    }
+}
+
+# Add bcache module, if needed.
+my @bcacheDevices = glob("/dev/bcache*");
+if (scalar @bcacheDevices > 0) {
+    push @initrdAvailableKernelModules, "bcache";
+}
+
+# Prevent unbootable systems if LVM snapshots are present at boot time.
+if (`lsblk -o TYPE` =~ "lvm") {
+    push @initrdKernelModules, "dm-snapshot";
+}
+
+my $virt = `@detectvirt@`;
+chomp $virt;
+
+
+# Check if we're a VirtualBox guest.  If so, enable the guest
+# additions.
+if ($virt eq "oracle") {
+    push @attrs, "virtualisation.virtualbox.guest.enable = true;"
+}
+
+
+# Likewise for QEMU.
+if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") {
+    push @imports, "(modulesPath + \"/profiles/qemu-guest.nix\")";
+}
+
+# Also for Hyper-V.
+if ($virt eq "microsoft") {
+    push @attrs, "virtualisation.hypervGuest.enable = true;"
+}
+
+
+# Pull in NixOS configuration for containers.
+if ($virt eq "systemd-nspawn") {
+    push @attrs, "boot.isContainer = true;";
+}
+
+
+# Provide firmware for devices that are not detected by this script,
+# unless we're in a VM/container.
+push @imports, "(modulesPath + \"/installer/scan/not-detected.nix\")"
+    if $virt eq "none";
+
+
+# For a device name like /dev/sda1, find a more stable path like
+# /dev/disk/by-uuid/X or /dev/disk/by-label/Y.
+sub findStableDevPath {
+    my ($dev) = @_;
+    return $dev if substr($dev, 0, 1) ne "/";
+    return $dev unless -e $dev;
+
+    my $st = stat($dev) or return $dev;
+
+    foreach my $dev2 (glob("/dev/disk/by-uuid/*"), glob("/dev/mapper/*"), glob("/dev/disk/by-label/*")) {
+        my $st2 = stat($dev2) or next;
+        return $dev2 if $st->rdev == $st2->rdev;
+    }
+
+    return $dev;
+}
+
+push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
+
+# Generate the swapDevices option from the currently activated swap
+# devices.
+my @swaps = read_file("/proc/swaps", err_mode => 'carp');
+my @swapDevices;
+if (@swaps) {
+    shift @swaps;
+    foreach my $swap (@swaps) {
+        my @fields = split ' ', $swap;
+        my $swapFilename = $fields[0];
+        my $swapType = $fields[1];
+        next unless -e $swapFilename;
+        my $dev = findStableDevPath $swapFilename;
+        if ($swapType =~ "partition") {
+            # zram devices are more likely created by configuration.nix, so
+            # ignore them here
+            next if ($swapFilename =~ /^\/dev\/zram/);
+            push @swapDevices, "{ device = \"$dev\"; }";
+        } elsif ($swapType =~ "file") {
+            # swap *files* are more likely specified in configuration.nix, so
+            # ignore them here.
+        } else {
+            die "Unsupported swap type: $swapType\n";
+        }
+    }
+}
+
+
+# Generate the fileSystems option from the currently mounted
+# filesystems.
+sub in {
+    my ($d1, $d2) = @_;
+    return $d1 eq $d2 || substr($d1, 0, length($d2) + 1) eq "$d2/";
+}
+
+my $fileSystems;
+my %fsByDev;
+foreach my $fs (read_file("/proc/self/mountinfo")) {
+    chomp $fs;
+    my @fields = split / /, $fs;
+    my $mountPoint = $fields[4];
+    $mountPoint =~ s/\\040/ /g; # account for mount points with spaces in the name (\040 is the escape character)
+    $mountPoint =~ s/\\011/\t/g; # account for mount points with tabs in the name (\011 is the escape character)
+    next unless -d $mountPoint;
+    my @mountOptions = split /,/, $fields[5];
+
+    next if !in($mountPoint, $rootDir);
+    $mountPoint = substr($mountPoint, length($rootDir)); # strip the root directory (e.g. /mnt)
+    $mountPoint = "/" if $mountPoint eq "";
+
+    # Skip special filesystems.
+    next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run") || $mountPoint eq "/var/lib/nfs/rpc_pipefs";
+
+    # Skip the optional fields.
+    my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
+    my $fsType = $fields[$n];
+    my $device = $fields[$n + 1];
+    my @superOptions = split /,/, $fields[$n + 2];
+    $device =~ s/\\040/ /g; # account for devices with spaces in the name (\040 is the escape character)
+    $device =~ s/\\011/\t/g; # account for mount points with tabs in the name (\011 is the escape character)
+
+    # Skip the read-only bind-mount on /nix/store.
+    next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions) && (grep { $_ eq "ro" } @mountOptions);
+
+    # Maybe this is a bind-mount of a filesystem we saw earlier?
+    if (defined $fsByDev{$fields[2]}) {
+        # Make sure this isn't a btrfs subvolume.
+        my $msg = `@btrfs@ subvol show $rootDir$mountPoint`;
+        if ($? != 0 || $msg =~ /ERROR:/s) {
+            my $path = $fields[3]; $path = "" if $path eq "/";
+            my $base = $fsByDev{$fields[2]};
+            $base = "" if $base eq "/";
+            $fileSystems .= <<EOF;
+  fileSystems.\"$mountPoint\" =
+    { device = \"$base$path\";
+      fsType = \"none\";
+      options = \[ \"bind\" \];
+    };
+
+EOF
+            next;
+        }
+    }
+    $fsByDev{$fields[2]} = $mountPoint;
+
+    # We don't know how to handle FUSE filesystems.
+    if ($fsType eq "fuseblk" || $fsType eq "fuse") {
+        print STDERR "warning: don't know how to emit ‘fileSystem’ option for FUSE filesystem ‘$mountPoint’\n";
+        next;
+    }
+
+    # Is this a mount of a loopback device?
+    my @extraOptions;
+    if ($device =~ /\/dev\/loop(\d+)/) {
+        my $loopnr = $1;
+        my $backer = read_file "/sys/block/loop$loopnr/loop/backing_file";
+        if (defined $backer) {
+            chomp $backer;
+            $device = $backer;
+            push @extraOptions, "loop";
+        }
+    }
+
+    # Is this a btrfs filesystem?
+    if ($fsType eq "btrfs") {
+        my ($status, @info) = runCommand("@btrfs@ subvol show $rootDir$mountPoint");
+        if ($status != 0 || join("", @info) =~ /ERROR:/) {
+            die "Failed to retrieve subvolume info for $mountPoint\n";
+        }
+        my @ids = join("\n", @info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
+        if ($#ids > 0) {
+            die "Btrfs subvol name for $mountPoint listed multiple times in mount\n"
+        } elsif ($#ids == 0) {
+            my @paths = join("", @info) =~ m/^([^\n]*)/;
+            if ($#paths > 0) {
+                die "Btrfs returned multiple paths for a single subvolume id, mountpoint $mountPoint\n";
+            } elsif ($#paths != 0) {
+                die "Btrfs did not return a path for the subvolume at $mountPoint\n";
+            }
+            push @extraOptions, "subvol=$paths[0]";
+        }
+    }
+
+    # Don't emit tmpfs entry for /tmp, because it most likely comes from the
+    # boot.tmpOnTmpfs option in configuration.nix (managed declaratively).
+    next if ($mountPoint eq "/tmp" && $fsType eq "tmpfs");
+
+    # Emit the filesystem.
+    $fileSystems .= <<EOF;
+  fileSystems.\"$mountPoint\" =
+    { device = \"${\(findStableDevPath $device)}\";
+      fsType = \"$fsType\";
+EOF
+
+    if (scalar @extraOptions > 0) {
+        $fileSystems .= <<EOF;
+      options = \[ ${\join " ", map { "\"" . $_ . "\"" } uniq(@extraOptions)} \];
+EOF
+    }
+
+    $fileSystems .= <<EOF;
+    };
+
+EOF
+
+    # If this filesystem is on a LUKS device, then add a
+    # boot.initrd.luks.devices entry.
+    if (-e $device) {
+        my $deviceName = basename(abs_path($device));
+        if (-e "/sys/class/block/$deviceName"
+            && read_file("/sys/class/block/$deviceName/dm/uuid",  err_mode => 'quiet') =~ /^CRYPT-LUKS/)
+        {
+            my @slaves = glob("/sys/class/block/$deviceName/slaves/*");
+            if (scalar @slaves == 1) {
+                my $slave = "/dev/" . basename($slaves[0]);
+                if (-e $slave) {
+                    my $dmName = read_file("/sys/class/block/$deviceName/dm/name");
+                    chomp $dmName;
+                    # Ensure to add an entry only once
+                    my $luksDevice = "  boot.initrd.luks.devices.\"$dmName\".device";
+                    if ($fileSystems !~ /^\Q$luksDevice\E/m) {
+                        $fileSystems .= "$luksDevice = \"${\(findStableDevPath $slave)}\";\n\n";
+                    }
+                }
+            }
+        }
+    }
+}
+
+# For lack of a better way to determine it, guess whether we should use a
+# bigger font for the console from the display mode on the first
+# framebuffer. A way based on the physical size/actual DPI reported by
+# the monitor would be nice, but I don't know how to do this without X :)
+my $fb_modes_file = "/sys/class/graphics/fb0/modes";
+if (-f $fb_modes_file && -r $fb_modes_file) {
+    my $modes = read_file($fb_modes_file);
+    $modes =~ m/([0-9]+)x([0-9]+)/;
+    my $console_width = $1, my $console_height = $2;
+    if ($console_width > 1920) {
+        push @attrs, "# high-resolution display";
+        push @attrs, 'hardware.video.hidpi.enable = lib.mkDefault true;';
+    }
+}
+
+
+# Generate the hardware configuration file.
+
+sub toNixStringList {
+    my $res = "";
+    foreach my $s (@_) {
+        $res .= " \"$s\"";
+    }
+    return $res;
+}
+sub toNixList {
+    my $res = "";
+    foreach my $s (@_) {
+        $res .= " $s";
+    }
+    return $res;
+}
+
+sub multiLineList {
+    my $indent = shift;
+    return " [ ]" if !@_;
+    my $res = "\n${indent}[ ";
+    my $first = 1;
+    foreach my $s (@_) {
+        $res .= "$indent  " if !$first;
+        $first = 0;
+        $res .= "$s\n";
+    }
+    $res .= "$indent]";
+    return $res;
+}
+
+my $initrdAvailableKernelModules = toNixStringList(uniq @initrdAvailableKernelModules);
+my $initrdKernelModules = toNixStringList(uniq @initrdKernelModules);
+my $kernelModules = toNixStringList(uniq @kernelModules);
+my $modulePackages = toNixList(uniq @modulePackages);
+
+my $fsAndSwap = "";
+if (!$noFilesystems) {
+    $fsAndSwap = "\n$fileSystems  ";
+    $fsAndSwap .= "swapDevices =" . multiLineList("    ", @swapDevices) . ";\n";
+}
+
+my $networkingDhcpConfig = generateNetworkingDhcpConfig();
+
+my $hwConfig = <<EOF;
+# Do not modify this file!  It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations.  Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports =${\multiLineList("    ", @imports)};
+
+  boot.initrd.availableKernelModules = [$initrdAvailableKernelModules ];
+  boot.initrd.kernelModules = [$initrdKernelModules ];
+  boot.kernelModules = [$kernelModules ];
+  boot.extraModulePackages = [$modulePackages ];
+$fsAndSwap
+$networkingDhcpConfig
+${\join "", (map { "  $_\n" } (uniq @attrs))}}
+EOF
+
+sub generateNetworkingDhcpConfig {
+    my $config = <<EOF;
+  # The global useDHCP flag is deprecated, therefore explicitly set to false here.
+  # Per-interface useDHCP will be mandatory in the future, so this generated config
+  # replicates the default behaviour.
+  networking.useDHCP = lib.mkDefault false;
+EOF
+
+    foreach my $path (glob "/sys/class/net/*") {
+        my $dev = basename($path);
+        if ($dev ne "lo") {
+            $config .= "  networking.interfaces.$dev.useDHCP = lib.mkDefault true;\n";
+        }
+    }
+
+    return $config;
+}
+
+sub generateXserverConfig {
+    my $xserverEnabled = "@xserverEnabled@";
+
+    my $config = "";
+    if ($xserverEnabled eq "1") {
+        $config = <<EOF;
+  # Enable the X11 windowing system.
+  services.xserver.enable = true;
+EOF
+    } else {
+        $config = <<EOF;
+  # Enable the X11 windowing system.
+  # services.xserver.enable = true;
+EOF
+    }
+}
+
+if ($showHardwareConfig) {
+    print STDOUT $hwConfig;
+} else {
+    $outDir = "$rootDir$outDir";
+
+    my $fn = "$outDir/hardware-configuration.nix";
+    print STDERR "writing $fn...\n";
+    mkpath($outDir, 0, 0755);
+    write_file($fn, $hwConfig);
+
+    # Generate a basic configuration.nix, unless one already exists.
+    $fn = "$outDir/configuration.nix";
+    if ($force || ! -e $fn) {
+        print STDERR "writing $fn...\n";
+
+        my $bootLoaderConfig = "";
+        if (-e "/sys/firmware/efi/efivars") {
+            $bootLoaderConfig = <<EOF;
+  # Use the systemd-boot EFI boot loader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+EOF
+        } elsif (-e "/boot/extlinux") {
+            $bootLoaderConfig = <<EOF;
+  # Use the extlinux boot loader. (NixOS wants to enable GRUB by default)
+  boot.loader.grub.enable = false;
+  # Enables the generation of /boot/extlinux/extlinux.conf
+  boot.loader.generic-extlinux-compatible.enable = true;
+EOF
+        } elsif ($virt ne "systemd-nspawn") {
+            $bootLoaderConfig = <<EOF;
+  # Use the GRUB 2 boot loader.
+  boot.loader.grub.enable = true;
+  boot.loader.grub.version = 2;
+  # boot.loader.grub.efiSupport = true;
+  # boot.loader.grub.efiInstallAsRemovable = true;
+  # boot.loader.efi.efiSysMountPoint = "/boot/efi";
+  # Define on which hard drive you want to install Grub.
+  # boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
+EOF
+        }
+
+        my $networkingDhcpConfig = generateNetworkingDhcpConfig();
+
+        my $xserverConfig = generateXserverConfig();
+
+        (my $desktopConfiguration = <<EOF)=~s/^/  /gm;
+@desktopConfiguration@
+EOF
+
+        write_file($fn, <<EOF);
+@configuration@
+EOF
+        print STDERR "For more hardware-specific settings, see https://github.com/NixOS/nixos-hardware.\n"
+    } else {
+        print STDERR "warning: not overwriting existing $fn\n";
+    }
+}
+
+# workaround for a bug in substituteAll
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
new file mode 100644
index 00000000000..e7cf52f5e32
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -0,0 +1,218 @@
+#! @runtimeShell@
+# shellcheck shell=bash
+
+set -e
+shopt -s nullglob
+
+export PATH=@path@:$PATH
+
+# Ensure a consistent umask.
+umask 0022
+
+# Parse the command line for the -I flag
+extraBuildFlags=()
+flakeFlags=()
+
+mountPoint=/mnt
+channelPath=
+system=
+verbosity=()
+
+while [ "$#" -gt 0 ]; do
+    i="$1"; shift 1
+    case "$i" in
+        --max-jobs|-j|--cores|-I|--substituters)
+            j="$1"; shift 1
+            extraBuildFlags+=("$i" "$j")
+            ;;
+        --option)
+            j="$1"; shift 1
+            k="$1"; shift 1
+            extraBuildFlags+=("$i" "$j" "$k")
+            ;;
+        --root)
+            mountPoint="$1"; shift 1
+            ;;
+        --system|--closure)
+            system="$1"; shift 1
+            ;;
+        --flake)
+          flake="$1"
+          flakeFlags=(--experimental-features 'nix-command flakes')
+          shift 1
+          ;;
+        --recreate-lock-file|--no-update-lock-file|--no-write-lock-file|--no-registries|--commit-lock-file)
+          lockFlags+=("$i")
+          ;;
+        --update-input)
+          j="$1"; shift 1
+          lockFlags+=("$i" "$j")
+          ;;
+        --override-input)
+          j="$1"; shift 1
+          k="$1"; shift 1
+          lockFlags+=("$i" "$j" "$k")
+          ;;
+        --channel)
+            channelPath="$1"; shift 1
+            ;;
+        --no-channel-copy)
+            noChannelCopy=1
+            ;;
+        --no-root-password|--no-root-passwd)
+            noRootPasswd=1
+            ;;
+        --no-bootloader)
+            noBootLoader=1
+            ;;
+        --show-trace|--impure|--keep-going)
+            extraBuildFlags+=("$i")
+            ;;
+        --help)
+            exec man nixos-install
+            exit 1
+            ;;
+        --debug)
+            set -x
+            ;;
+        -v*|--verbose)
+            verbosity+=("$i")
+            ;;
+        *)
+            echo "$0: unknown option \`$i'"
+            exit 1
+            ;;
+    esac
+done
+
+if ! test -e "$mountPoint"; then
+    echo "mount point $mountPoint doesn't exist"
+    exit 1
+fi
+
+# Verify permissions are okay-enough
+checkPath="$(realpath "$mountPoint")"
+while [[ "$checkPath" != "/" ]]; do
+    mode="$(stat -c '%a' "$checkPath")"
+    if [[ "${mode: -1}" -lt "5" ]]; then
+        echo "path $checkPath should have permissions 755, but had permissions $mode. Consider running 'chmod o+rx $checkPath'."
+        exit 1
+    fi
+    checkPath="$(dirname "$checkPath")"
+done
+
+# Get the path of the NixOS configuration file.
+if [[ -z $NIXOS_CONFIG ]]; then
+    NIXOS_CONFIG=$mountPoint/etc/nixos/configuration.nix
+fi
+
+if [[ ${NIXOS_CONFIG:0:1} != / ]]; then
+    echo "$0: \$NIXOS_CONFIG is not an absolute path"
+    exit 1
+fi
+
+if [[ -n $flake ]]; then
+    if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
+       flake="${BASH_REMATCH[1]}"
+       flakeAttr="${BASH_REMATCH[2]}"
+    fi
+    if [[ -z "$flakeAttr" ]]; then
+        echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri."
+        echo "For example, to use the output nixosConfigurations.foo from the flake.nix, append \"#foo\" to the flake-uri."
+        exit 1
+    fi
+    flakeAttr="nixosConfigurations.\"$flakeAttr\""
+fi
+
+# Resolve the flake.
+if [[ -n $flake ]]; then
+    flake=$(nix "${flakeFlags[@]}" flake metadata --json "${extraBuildFlags[@]}" "${lockFlags[@]}" -- "$flake" | jq -r .url)
+fi
+
+if [[ ! -e $NIXOS_CONFIG && -z $system && -z $flake ]]; then
+    echo "configuration file $NIXOS_CONFIG doesn't exist"
+    exit 1
+fi
+
+# A place to drop temporary stuff.
+tmpdir="$(mktemp -d -p "$mountPoint")"
+trap 'rm -rf $tmpdir' EXIT
+
+# store temporary files on target filesystem by default
+export TMPDIR=${TMPDIR:-$tmpdir}
+
+sub="auto?trusted=1"
+
+# Copy the NixOS/Nixpkgs sources to the target as the initial contents
+# of the NixOS channel.
+if [[ -z $noChannelCopy ]]; then
+    if [[ -z $channelPath ]]; then
+        channelPath="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")"
+    fi
+    if [[ -n $channelPath ]]; then
+        echo "copying channel..."
+        mkdir -p "$mountPoint"/nix/var/nix/profiles/per-user/root
+        nix-env --store "$mountPoint" "${extraBuildFlags[@]}" --extra-substituters "$sub" \
+                -p "$mountPoint"/nix/var/nix/profiles/per-user/root/channels --set "$channelPath" --quiet \
+                "${verbosity[@]}"
+        install -m 0700 -d "$mountPoint"/root/.nix-defexpr
+        ln -sfn /nix/var/nix/profiles/per-user/root/channels "$mountPoint"/root/.nix-defexpr/channels
+    fi
+fi
+
+# Build the system configuration in the target filesystem.
+if [[ -z $system ]]; then
+    outLink="$tmpdir/system"
+    if [[ -z $flake ]]; then
+        echo "building the configuration in $NIXOS_CONFIG..."
+        nix-build --out-link "$outLink" --store "$mountPoint" "${extraBuildFlags[@]}" \
+            --extra-substituters "$sub" \
+            '<nixpkgs/nixos>' -A system -I "nixos-config=$NIXOS_CONFIG" "${verbosity[@]}"
+    else
+        echo "building the flake in $flake..."
+        nix "${flakeFlags[@]}" build "$flake#$flakeAttr.config.system.build.toplevel" \
+            --store "$mountPoint" --extra-substituters "$sub" "${verbosity[@]}" \
+            "${extraBuildFlags[@]}" "${lockFlags[@]}" --out-link "$outLink"
+    fi
+    system=$(readlink -f "$outLink")
+fi
+
+# Set the system profile to point to the configuration. TODO: combine
+# this with the previous step once we have a nix-env replacement with
+# a progress bar.
+nix-env --store "$mountPoint" "${extraBuildFlags[@]}" \
+        --extra-substituters "$sub" \
+        -p "$mountPoint"/nix/var/nix/profiles/system --set "$system" "${verbosity[@]}"
+
+# Mark the target as a NixOS installation, otherwise switch-to-configuration will chicken out.
+mkdir -m 0755 -p "$mountPoint/etc"
+touch "$mountPoint/etc/NIXOS"
+
+# Switch to the new system configuration.  This will install Grub with
+# a menu default pointing at the kernel/initrd/etc of the new
+# configuration.
+if [[ -z $noBootLoader ]]; then
+    echo "installing the boot loader..."
+    # Grub needs an mtab.
+    ln -sfn /proc/mounts "$mountPoint"/etc/mtab
+    NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root "$mountPoint" -- /run/current-system/bin/switch-to-configuration boot
+fi
+
+# Ask the user to set a root password, but only if the passwd command
+# exists (i.e. when mutable user accounts are enabled).
+if [[ -z $noRootPasswd ]] && [ -t 0 ]; then
+    if nixos-enter --root "$mountPoint" -c 'test -e /nix/var/nix/profiles/system/sw/bin/passwd'; then
+        set +e
+        nixos-enter --root "$mountPoint" -c 'echo "setting root password..." && /nix/var/nix/profiles/system/sw/bin/passwd'
+        exit_code=$?
+        set -e
+
+        if [[ $exit_code != 0 ]]; then
+            echo "Setting a root password failed with the above printed error."
+            echo "You can set the root password manually by executing \`nixos-enter --root ${mountPoint@Q}\` and then running \`passwd\` in the shell of the new system."
+            exit $exit_code
+        fi
+    fi
+fi
+
+echo "installation finished!"
diff --git a/nixos/modules/installer/tools/nixos-option/default.nix b/nixos/modules/installer/tools/nixos-option/default.nix
new file mode 100644
index 00000000000..061460f38a3
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-option/default.nix
@@ -0,0 +1 @@
+{ pkgs, ... }: pkgs.nixos-option
diff --git a/nixos/modules/installer/tools/nixos-version.sh b/nixos/modules/installer/tools/nixos-version.sh
new file mode 100644
index 00000000000..59a9c572b41
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-version.sh
@@ -0,0 +1,24 @@
+#! @runtimeShell@
+# shellcheck shell=bash
+
+case "$1" in
+  -h|--help)
+    exec man nixos-version
+    exit 1
+    ;;
+  --hash|--revision)
+    if ! [[ @revision@ =~ ^[0-9a-f]+$ ]]; then
+      echo "$0: Nixpkgs commit hash is unknown"
+      exit 1
+    fi
+    echo "@revision@"
+    ;;
+  --json)
+    cat <<EOF
+@json@
+EOF
+    ;;
+  *)
+    echo "@version@ (@codeName@)"
+    ;;
+esac
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
new file mode 100644
index 00000000000..71aaf7f253d
--- /dev/null
+++ b/nixos/modules/installer/tools/tools.nix
@@ -0,0 +1,235 @@
+# This module generates nixos-install, nixos-rebuild,
+# nixos-generate-config, etc.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  makeProg = args: pkgs.substituteAll (args // {
+    dir = "bin";
+    isExecutable = true;
+  });
+
+  nixos-build-vms = makeProg {
+    name = "nixos-build-vms";
+    src = ./nixos-build-vms/nixos-build-vms.sh;
+    inherit (pkgs) runtimeShell;
+  };
+
+  nixos-install = makeProg {
+    name = "nixos-install";
+    src = ./nixos-install.sh;
+    inherit (pkgs) runtimeShell;
+    nix = config.nix.package.out;
+    path = makeBinPath [
+      pkgs.jq
+      nixos-enter
+    ];
+  };
+
+  nixos-rebuild = pkgs.nixos-rebuild.override { nix = config.nix.package.out; };
+
+  nixos-generate-config = makeProg {
+    name = "nixos-generate-config";
+    src = ./nixos-generate-config.pl;
+    perl = "${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl";
+    detectvirt = "${pkgs.systemd}/bin/systemd-detect-virt";
+    btrfs = "${pkgs.btrfs-progs}/bin/btrfs";
+    inherit (config.system.nixos-generate-config) configuration desktopConfiguration;
+    xserverEnabled = config.services.xserver.enable;
+  };
+
+  nixos-option =
+    if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
+    then null
+    else pkgs.nixos-option;
+
+  nixos-version = makeProg {
+    name = "nixos-version";
+    src = ./nixos-version.sh;
+    inherit (pkgs) runtimeShell;
+    inherit (config.system.nixos) version codeName revision;
+    inherit (config.system) configurationRevision;
+    json = builtins.toJSON ({
+      nixosVersion = config.system.nixos.version;
+    } // optionalAttrs (config.system.nixos.revision != null) {
+      nixpkgsRevision = config.system.nixos.revision;
+    } // optionalAttrs (config.system.configurationRevision != null) {
+      configurationRevision = config.system.configurationRevision;
+    });
+  };
+
+  nixos-enter = makeProg {
+    name = "nixos-enter";
+    src = ./nixos-enter.sh;
+    inherit (pkgs) runtimeShell;
+  };
+
+in
+
+{
+
+  options.system.nixos-generate-config = {
+    configuration = mkOption {
+      internal = true;
+      type = types.str;
+      description = ''
+        The NixOS module that <literal>nixos-generate-config</literal>
+        saves to <literal>/etc/nixos/configuration.nix</literal>.
+
+        This is an internal option. No backward compatibility is guaranteed.
+        Use at your own risk!
+
+        Note that this string gets spliced into a Perl script. The perl
+        variable <literal>$bootLoaderConfig</literal> can be used to
+        splice in the boot loader configuration.
+      '';
+    };
+
+    desktopConfiguration = mkOption {
+      internal = true;
+      type = types.listOf types.lines;
+      default = [];
+      description = ''
+        Text to preseed the desktop configuration that <literal>nixos-generate-config</literal>
+        saves to <literal>/etc/nixos/configuration.nix</literal>.
+
+        This is an internal option. No backward compatibility is guaranteed.
+        Use at your own risk!
+
+        Note that this string gets spliced into a Perl script. The perl
+        variable <literal>$bootLoaderConfig</literal> can be used to
+        splice in the boot loader configuration.
+      '';
+    };
+  };
+
+  options.system.disableInstallerTools = mkOption {
+    internal = true;
+    type = types.bool;
+    default = false;
+    description = ''
+      Disable nixos-rebuild, nixos-generate-config, nixos-installer
+      and other NixOS tools. This is useful to shrink embedded,
+      read-only systems which are not expected to be rebuild or
+      reconfigure themselves. Use at your own risk!
+    '';
+  };
+
+  config = lib.mkIf (!config.system.disableInstallerTools) {
+
+    system.nixos-generate-config.configuration = mkDefault ''
+      # Edit this configuration file to define what should be installed on
+      # your system.  Help is available in the configuration.nix(5) man page
+      # and in the NixOS manual (accessible by running ‘nixos-help’).
+
+      { config, pkgs, ... }:
+
+      {
+        imports =
+          [ # Include the results of the hardware scan.
+            ./hardware-configuration.nix
+          ];
+
+      $bootLoaderConfig
+        # networking.hostName = "nixos"; # Define your hostname.
+        # Pick only one of the below networking options.
+        # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
+        # networking.networkmanager.enable = true;  # Easiest to use and most distros use this by default.
+
+        # Set your time zone.
+        # time.timeZone = "Europe/Amsterdam";
+
+        # Configure network proxy if necessary
+        # networking.proxy.default = "http://user:password\@proxy:port/";
+        # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
+
+        # Select internationalisation properties.
+        # i18n.defaultLocale = "en_US.UTF-8";
+        # console = {
+        #   font = "Lat2-Terminus16";
+        #   keyMap = "us";
+        #   useXkbConfig = true; # use xkbOptions in tty.
+        # };
+
+      $xserverConfig
+
+      $desktopConfiguration
+        # Configure keymap in X11
+        # services.xserver.layout = "us";
+        # services.xserver.xkbOptions = {
+        #   "eurosign:e";
+        #   "caps:escape" # map caps to escape.
+        # };
+
+        # Enable CUPS to print documents.
+        # services.printing.enable = true;
+
+        # Enable sound.
+        # sound.enable = true;
+        # hardware.pulseaudio.enable = true;
+
+        # Enable touchpad support (enabled default in most desktopManager).
+        # services.xserver.libinput.enable = true;
+
+        # Define a user account. Don't forget to set a password with ‘passwd’.
+        # users.users.jane = {
+        #   isNormalUser = true;
+        #   extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
+        # };
+
+        # List packages installed in system profile. To search, run:
+        # \$ nix search wget
+        # environment.systemPackages = with pkgs; [
+        #   vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
+        #   wget
+        #   firefox
+        # ];
+
+        # Some programs need SUID wrappers, can be configured further or are
+        # started in user sessions.
+        # programs.mtr.enable = true;
+        # programs.gnupg.agent = {
+        #   enable = true;
+        #   enableSSHSupport = true;
+        # };
+
+        # List services that you want to enable:
+
+        # Enable the OpenSSH daemon.
+        # services.openssh.enable = true;
+
+        # Open ports in the firewall.
+        # networking.firewall.allowedTCPPorts = [ ... ];
+        # networking.firewall.allowedUDPPorts = [ ... ];
+        # Or disable the firewall altogether.
+        # networking.firewall.enable = false;
+
+        # This value determines the NixOS release from which the default
+        # settings for stateful data, like file locations and database versions
+        # on your system were taken. It‘s perfectly fine and recommended to leave
+        # this value at the release version of the first install of this system.
+        # Before changing this value read the documentation for this option
+        # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
+        system.stateVersion = "${config.system.nixos.release}"; # Did you read the comment?
+
+      }
+    '';
+
+    environment.systemPackages =
+      [ nixos-build-vms
+        nixos-install
+        nixos-rebuild
+        nixos-generate-config
+        nixos-version
+        nixos-enter
+      ] ++ lib.optional (nixos-option != null) nixos-option;
+
+    system.build = {
+      inherit nixos-install nixos-generate-config nixos-option nixos-rebuild nixos-enter;
+    };
+
+  };
+
+}