├── .gitignore ├── LICENSE.md ├── README.md ├── keys ├── add-key.sh └── keys │ ├── ekleog │ └── nadrieril ├── nixtos ├── block-device │ ├── default.nix │ └── virtio-disk │ │ └── default.nix ├── bootloader │ ├── default.nix │ └── grub-bios │ │ └── default.nix ├── bootloaders │ └── default.nix ├── build-vm │ └── default.nix ├── core-system │ └── default.nix ├── default.nix ├── files │ └── default.nix ├── filesystem │ ├── default.nix │ ├── ext4 │ │ └── default.nix │ ├── overlayfs │ │ └── default.nix │ ├── tmpfs │ │ └── default.nix │ └── virtfs │ │ └── default.nix ├── groups │ ├── default.nix │ └── unix │ │ └── default.nix ├── init │ ├── default.nix │ └── runit │ │ └── default.nix ├── lib │ ├── default.nix │ ├── make-initrd │ │ └── default.nix │ ├── merges │ │ └── default.nix │ ├── solve-block-devices │ │ └── default.nix │ ├── solve-filesystems │ │ └── default.nix │ ├── solve-services │ │ └── default.nix │ └── types │ │ └── default.nix ├── operating-system │ └── default.nix ├── pam │ ├── default.nix │ ├── env.nix │ └── service.nix ├── service-graph │ └── default.nix ├── tty │ ├── agetty │ │ └── default.nix │ └── default.nix ├── udev │ ├── default.nix │ └── eudev │ │ └── default.nix ├── users │ ├── default.nix │ └── unix │ │ └── default.nix ├── version │ └── default.nix └── vm-drive │ ├── default.nix │ ├── empty-drive │ └── default.nix │ ├── guestfish │ └── default.nix │ ├── virtfs-to-store │ └── default.nix │ └── virtfs │ └── default.nix └── tests ├── default.nix ├── example.nix ├── example └── configuration.nix └── lib ├── default.nix ├── disjoint-union.nix ├── make-attrset.nix ├── merges.nix ├── sorted-deps-of.nix └── types.nix /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | version 3 | *.img 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Leo Gaspard 2 | Copyright 2018 Nadrieril Feneanar 3 | Copyright 2018 Profpatsch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NixtOS 2 | 3 | NixtOS, the next-generation NixOS that builds on both GuixSD concepts on 4 | steroids and nixpkgs. Currently requires a rewrite of every module, very far 5 | from being production-ready. 6 | 7 | The objectives are (from most fundamental to most user-facing): 8 | * Clearly defined dependencies between modules 9 | * Modularity in the modules: each module should be possible to take out to 10 | replace it by another module fulfilling the same interface 11 | * Mixing modules from multiple release channels (eg. core system from stable, 12 | but unstable services like matrix-synapse from unstable), feature which 13 | naturally follows from modularity 14 | -------------------------------------------------------------------------------- /keys/add-key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gpg2 --export --export-options export-minimal --armor "$@" 4 | 5 | if [ -t 1 ]; then 6 | echo "" >&2 7 | echo "/!\\ /!\\" >&2 8 | echo "You should run this script by redirecting its output to a file in ‘$(dirname $0)/keys’" >&2 9 | echo "/!\\ /!\\" >&2 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /keys/keys/ekleog: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 198 | =x3b6 199 | -----END PGP PUBLIC KEY BLOCK----- 200 | -------------------------------------------------------------------------------- /keys/keys/nadrieril: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 280 | =VxCo 281 | -----END PGP PUBLIC KEY BLOCK----- 282 | -------------------------------------------------------------------------------- /nixtos/block-device/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | virtio-disk = import ./virtio-disk { inherit pkgs top; }; 5 | } 6 | -------------------------------------------------------------------------------- /nixtos/block-device/virtio-disk/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { }: 4 | 5 | { 6 | depends-on = []; 7 | 8 | extra-modules = [ "virtio-pci" "virtio-blk" ]; 9 | 10 | build-command = device: ""; 11 | } 12 | -------------------------------------------------------------------------------- /nixtos/bootloader/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | grub-bios = import ./grub-bios { inherit pkgs top; }; 5 | } 6 | -------------------------------------------------------------------------------- /nixtos/bootloader/grub-bios/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | block-device, # The (linux) block device on which to install GRUB 5 | config-dir, # The path to the directory (each mount point must be used only 6 | # once where GRUB will put its config files 7 | # TODO(high): This has no reason to be there, disks should be just search'd 8 | config-dir-grub-device, # The device name (for GRUB) the config-dir is on 9 | config-dir-grub-dir, # The path (with ${config-dir-grub-device} as the root) 10 | # to ${config-dir} 11 | os, # The OS to boot (TODO(high): that's not supposed to be there, but fetched 12 | # from the profile generations list?) 13 | }: 14 | 15 | let 16 | config = pkgs.writeText "grub.cfg" '' 17 | # TODO(medium): Handle grub-reboot 18 | 19 | menuentry "NixtOS - Default" { 20 | # TODO(high): Remove this console=ttyS0 argument if not requested for 21 | linux ${config-dir-grub-device}/${config-dir-grub-dir}/grub/kernel init=${os}/init console=ttyS0 22 | initrd ${config-dir-grub-device}/${config-dir-grub-dir}/grub/initrd 23 | } 24 | ''; 25 | in 26 | 27 | pkgs.writeScript "install-grub-bios" '' 28 | #!/bin/sh 29 | 30 | mkdir -p ${config-dir}/grub 31 | 32 | # TODO(medium): do not copy if it's already there 33 | cp ${os}/{kernel,initrd} ${config-dir}/grub 34 | 35 | # TODO(medium): Atomically change config 36 | cp ${config} ${config-dir}/grub/grub.cfg 37 | 38 | # TODO(medium): Do not re-install if it's already installed 39 | grub-install --boot-directory=${config-dir} ${block-device} 40 | '' 41 | -------------------------------------------------------------------------------- /nixtos/bootloaders/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | loaders: 4 | 5 | # TODO(medium): handle multiple bootloaders 6 | assert builtins.length loaders == 1; 7 | 8 | pkgs.writeScript "install-bootloaders" '' 9 | #!${pkgs.bash}/bin/bash 10 | 11 | ${pkgs.lib.concatStringsSep "\n" (map toString loaders)} 12 | '' 13 | -------------------------------------------------------------------------------- /nixtos/build-vm/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | os, 5 | extra-cmdline-args ? "", 6 | qemu ? "${pkgs.kvm}/bin/qemu-kvm", 7 | cpu ? "host", 8 | memory ? "1G", 9 | ncpu ? 1, 10 | net ? "none", 11 | drives, 12 | }: 13 | 14 | let 15 | name = "vm-${os.name}"; 16 | 17 | store-paths = pkgs.closureInfo { rootPaths = [ os ]; }; 18 | 19 | store = pkgs.runCommand "store-${name}" {} '' 20 | mkdir $out 21 | cp -r $(cat ${store-paths}/store-paths) $out 22 | ''; 23 | 24 | drive-builders = pkgs.lib.concatStringsSep "\n" ( 25 | map (f: (f { inherit store; }).build) drives 26 | ); 27 | 28 | drive-options = pkgs.lib.concatStringsSep " \\\n " ( 29 | map (f: (f { inherit store; }).options) drives 30 | ); 31 | 32 | net-options = 33 | if net == "none" then "" 34 | else if net == "user" then "-net nic,model=virtio -net user" 35 | else throw "Unknown net option ‘${net}’"; 36 | in 37 | pkgs.writeScript name '' 38 | #!${pkgs.bash}/bin/bash 39 | 40 | ${drive-builders} 41 | 42 | exec ${pkgs.kvm}/bin/qemu-kvm \ 43 | -cpu ${cpu} \ 44 | -name ${name} \ 45 | -m ${memory} \ 46 | -smp ${toString ncpu} \ 47 | -kernel ${os}/kernel \ 48 | -initrd ${os}/initrd \ 49 | -append 'init=${os}/init console=ttyS0 ${extra-cmdline-args}' \ 50 | -serial mon:stdio \ 51 | ${net-options} \ 52 | ${drive-options} 53 | '' 54 | -------------------------------------------------------------------------------- /nixtos/core-system/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | files ? top.files {}, 5 | init ? top.init.runit {}, 6 | users ? top.users.unix {}, 7 | groups ? top.groups.unix {}, 8 | pam ? top.pam {}, 9 | udev ? top.udev.eudev {}, 10 | ttys ? { 11 | tty1 = top.tty.agetty {}; 12 | tty2 = top.tty.agetty {}; 13 | tty3 = top.tty.agetty {}; 14 | tty4 = top.tty.agetty {}; 15 | tty5 = top.tty.agetty {}; 16 | tty6 = top.tty.agetty {}; 17 | }, 18 | }: 19 | 20 | services: 21 | 22 | # TODO(high): Handle logging (and add a /dev/log device for stuff used to it) 23 | 24 | top.lib.disjoint-union (d: 25 | throw "Passed service names that were already taken by core system: ${toString d}" 26 | ) services ( 27 | top.lib.disjoint-union (d: 28 | throw "Passed tty names that were already taken by core system: ${toString d}" 29 | ) { 30 | inherit files init users groups pam udev; 31 | } (pkgs.lib.mapAttrs (n: v: v n) ttys)) 32 | -------------------------------------------------------------------------------- /nixtos/default.nix: -------------------------------------------------------------------------------- 1 | # TODO(high): Add the equivalent of nixos-rebuild 2 | 3 | { pkgs, hooks ? {} }: 4 | 5 | # TODO(low): Cleanup this list by moving things to their right place 6 | let 7 | top = pkgs.lib.recursiveUpdate { 8 | block-device = import ./block-device { inherit pkgs top; }; 9 | bootloader = import ./bootloader { inherit pkgs top; }; 10 | bootloaders = import ./bootloaders { inherit pkgs top; }; 11 | build-vm = import ./build-vm { inherit pkgs top; }; 12 | core-system = import ./core-system { inherit pkgs top; }; 13 | files = import ./files { inherit pkgs top; }; 14 | filesystem = import ./filesystem { inherit pkgs top; }; 15 | groups = import ./groups { inherit pkgs top; }; 16 | init = import ./init { inherit pkgs top; }; 17 | lib = import ./lib { inherit pkgs top; }; 18 | operating-system = import ./operating-system { inherit pkgs top; }; 19 | pam = import ./pam { inherit pkgs top; }; 20 | service-graph = import ./service-graph { inherit pkgs top; }; 21 | tty = import ./tty { inherit pkgs top; }; 22 | udev = import ./udev { inherit pkgs top; }; 23 | users = import ./users { inherit pkgs top; }; 24 | version = import ./version { inherit pkgs top; }; 25 | vm-drive = import ./vm-drive { inherit pkgs top; }; 26 | } hooks; 27 | in 28 | top 29 | -------------------------------------------------------------------------------- /nixtos/files/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | assertions ? "assertions", 5 | activation-scripts ? "activation-scripts", 6 | } @ args: 7 | 8 | extenders: 9 | 10 | let 11 | # TODO(medium): make upgrades more atomic by doing like /etc/static 12 | files = top.lib.make-attrset (f: 13 | throw "Trying to define the same files at multiple positions: ${builtins.toJSON f}" 14 | ) (map (e: { name = e.file; value = e; }) extenders); 15 | in 16 | { 17 | ${assertions} = with top.lib.types; 18 | assert-type "nixtos.files's argument" args (product-opt { 19 | req = {}; 20 | opt = { 21 | assertions = string; 22 | activation-scripts = string; 23 | }; 24 | }); 25 | 26 | ${activation-scripts} = { 27 | meta.type = "script"; 28 | script = pkgs.lib.concatStringsSep "\n" (pkgs.lib.mapAttrsToList (file: d: 29 | ''mkdir -p "${dirOf file}"'' + "\n" + ( 30 | if d.meta.type == "symlink" then 31 | ''ln -s "${d.target}" "${file}"'' 32 | else 33 | throw "Unknown type for generating file ‘${file}’: ‘${d.type}’" 34 | ) 35 | ) files); 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /nixtos/filesystem/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | ext4 = import ./ext4 { inherit pkgs top; }; 5 | overlayfs = import ./overlayfs { inherit pkgs top; }; 6 | tmpfs = import ./tmpfs { inherit pkgs top; }; 7 | virtfs = import ./virtfs { inherit pkgs top; }; 8 | } 9 | -------------------------------------------------------------------------------- /nixtos/filesystem/ext4/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { block-device }: 4 | 5 | { 6 | wait-for-block-devices = [ block-device ]; 7 | wait-for-files = []; 8 | 9 | extra-modules = [ "ext4" ]; 10 | 11 | mount-command = root: mount: "mount -t ext4 ${block-device} ${root}${mount}"; 12 | } 13 | -------------------------------------------------------------------------------- /nixtos/filesystem/overlayfs/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { lower, upper, work }: 4 | 5 | { 6 | wait-for-block-devices = []; 7 | wait-for-files = [ lower upper work ]; 8 | 9 | extra-modules = [ "overlay" ]; 10 | 11 | # TODO(medium): remove these mkdir -p? 12 | mount-command = root: mount: '' 13 | mkdir -p ${root}${lower} ${root}${upper} ${root}${work} 14 | mount -t overlay none ${root}${mount} \ 15 | -o lowerdir=${root}${lower},upperdir=${root}${upper},workdir=${root}${work} 16 | ''; 17 | } 18 | -------------------------------------------------------------------------------- /nixtos/filesystem/tmpfs/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { size ? null }: 4 | 5 | let 6 | sz = if size == null then "50%" else toString size; 7 | in 8 | { 9 | wait-for-block-devices = []; 10 | wait-for-files = []; 11 | 12 | extra-modules = []; 13 | 14 | mount-command = root: mount: 15 | "mount -t tmpfs -o size=${sz} none ${root}${mount}"; 16 | } 17 | -------------------------------------------------------------------------------- /nixtos/filesystem/virtfs/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { tag }: 4 | 5 | # TODO(low): Figure out how to wait on something (file?) to be sure the fs 6 | # can be mounted before trying to mount it (but I can't imagine a way for the fs 7 | # not to be ready just after modprobe, so that's likely not a top-priority 8 | # issue) 9 | { 10 | wait-for-block-devices = []; 11 | wait-for-files = []; 12 | 13 | extra-modules = [ "9p" "9pnet_virtio" "virtio_pci" ]; 14 | 15 | # Accoding to The Internet™, 256KiB (= 262144B) works pretty well 16 | mount-command = root: mount: '' 17 | mount -t 9p -o trans=virtio,version=9p2000.L,msize=262144 ${tag} ${root}${mount} 18 | ''; 19 | } 20 | -------------------------------------------------------------------------------- /nixtos/groups/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | unix = import ./unix { inherit pkgs top; }; 5 | } 6 | -------------------------------------------------------------------------------- /nixtos/groups/unix/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | rec { 4 | # Configuration helpers 5 | # ===================== 6 | 7 | # groups: ({ name:string, groups:list group } | list group) -> service 8 | # TODO(low): typecheck? 9 | groups = arg: # TODO(low): abstract this away from unix? 10 | if builtins.isList arg then groups { name = "groups"; groups = arg; } 11 | else 12 | _ignored_extenders: 13 | { 14 | ${arg.name} = 15 | builtins.map (group: group // { meta.type = "group"; }) arg.groups; 16 | }; 17 | 18 | # Main implementation functor 19 | # =========================== 20 | 21 | __functor = self: { 22 | files ? "files" 23 | }: 24 | 25 | extenders: 26 | 27 | # TODO(low): Allow for imperative-style user&group definition 28 | # TODO(medium): The ‘users’ parameter should not be given here, but as an 29 | # ‘extra-groups’ parameter of the user (ie. as extenders). 30 | # TODO(medium): ‘gid’ should have a default value 31 | let 32 | # TODO(low): this builtins.all should be a call to lib.make-attrset for better 33 | # error reporting 34 | group-list = 35 | assert builtins.all (e: 36 | 1 == pkgs.lib.count (x: x.group == e.group) extenders && 37 | 1 == pkgs.lib.count (x: x.gid == e.gid) extenders 38 | ) extenders; 39 | map (e: 40 | assert e.meta.type == "group"; 41 | let users = pkgs.lib.concatStringsSep "," e.users; in 42 | "${e.group}:x:${toString e.gid}:${users}" 43 | ) extenders; 44 | 45 | group-text = pkgs.lib.concatStringsSep "\n" group-list; 46 | in 47 | { 48 | ${files} = { 49 | meta.type = "symlink"; 50 | file = "/etc/group"; 51 | target = pkgs.writeText "group" group-text; 52 | }; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /nixtos/init/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | { 3 | runit = import ./runit { inherit pkgs top; }; 4 | } 5 | -------------------------------------------------------------------------------- /nixtos/init/runit/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | assertions ? "assertions", 5 | kernel ? "kernel", 6 | files ? "files", 7 | } @ args: 8 | 9 | extenders: 10 | 11 | let 12 | asserts = with top.lib.types; 13 | assert-type "nixtos.init.runit's argument" args (product-opt { 14 | req = {}; 15 | opt = { 16 | kernel = string; 17 | files = string; 18 | assertions = string; 19 | }; 20 | }); 21 | 22 | # TODO(medium): compute `name` from the service name + given name 23 | services = top.lib.make-attrset (s: 24 | throw "Trying to define the same services at multiple locations: ${builtins.toJSON s}" 25 | ) (map (e: { name = e.name; value = e; }) extenders); 26 | 27 | services-dir = 28 | pkgs.runCommand "runit-services" {} ( 29 | '' 30 | mkdir $out 31 | '' + pkgs.lib.concatStringsSep "\n" (pkgs.lib.mapAttrsToList (service: d: 32 | # TODO(low): Currently this leads to a shell script exec'ing a shell 33 | # script exec'ing the result, thus one unneeded level of indirection 34 | # TODO(high): The ‘log-script’ thing is tightly linked with runit. It 35 | # shouldn't be. 36 | assert d.meta.type == "service"; '' 37 | mkdir "$out/${service}" 38 | 39 | ln -s "/run/runit/supervise-${service}" \ 40 | "$out/${service}/supervise" 41 | 42 | cat > "$out/${service}/run" < "$out/${service}/log/run" < /dev/null 63 | $out/bin/kmod -h | grep 'Usage:' > /dev/null 64 | $out/bin/modprobe -h | grep 'Usage:' > /dev/null 65 | ''; 66 | 67 | # TODO(low): Handle the case where someone was crazy enough to have a mount 68 | # point *below* /nix/store? 69 | # TODO(medium): Make command executed on error configurable 70 | init = pkgs.writeScript "initrd-init" '' 71 | #!${utils}/bin/ash -e 72 | PATH="${utils}/bin" 73 | 74 | function error_occured() { 75 | exec env PATH=$PATH ash 76 | } 77 | trap error_occured EXIT 78 | 79 | echo "Setting up basic environment" 80 | mount -t devtmpfs none /dev 81 | mount -t proc none /proc 82 | mount -t sysfs none /sys 83 | 84 | echo "Parsing command-line arguments" 85 | for opt in $(cat /proc/cmdline); do 86 | case $opt in 87 | init=*) 88 | init="$(echo "$opt" | sed 's/.*=//')" 89 | echo "Found init ‘$init’" 90 | ;; 91 | esac 92 | done 93 | 94 | echo "Loading requested modules" 95 | mkdir /lib 96 | ln -s ${module-closure}/lib/modules /lib/modules 97 | ${pkgs.lib.concatStringsSep "\n" (map (mod: "modprobe ${mod}") modules)} 98 | 99 | echo "Building block devices" 100 | ${pkgs.lib.concatStringsSep "\n" ( 101 | map solved-block-devices.build-and-wait-for 102 | solved-filesystems.initrd-block-devices 103 | )} 104 | 105 | echo "Mounting filesystems" 106 | ${solved-filesystems.mount-filesystems-for "/nix/store" "/real-root"} 107 | 108 | echo "Cleaning up" 109 | umount /sys 110 | umount /proc 111 | umount /dev 112 | 113 | echo "Switching to on-disk init" 114 | exec switch_root /real-root $init 115 | ''; 116 | in 117 | pkgs.makeInitrd { 118 | contents = [ 119 | { object = init; 120 | symlink = "/init"; 121 | } 122 | ]; 123 | } 124 | -------------------------------------------------------------------------------- /nixtos/lib/merges/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | # A merge function is list T -> { result = T'; errors = list error; } 4 | let 5 | # Merge function that ignores its arguments and just returns `value` 6 | const = value: l: { 7 | result = value; 8 | errors = []; 9 | }; 10 | 11 | # Merge attrsets by disjoint union 12 | attrs-disjoint-union = l: 13 | let 14 | # Either a set, if the disjoint-union passed, or a list, if the sets 15 | # weren't actually disjoint 16 | union = builtins.foldl' (top.lib.disjoint-union (e: e)) {} l; 17 | in 18 | if builtins.isList union then { 19 | errors = [ { 20 | path = []; 21 | error = "keys passed multiple times to disjoint union: " + 22 | pkgs.lib.generators.toPretty {} union; 23 | } ]; 24 | } else { 25 | result = union; 26 | errors = []; 27 | }; 28 | 29 | # Merge attrsets under each key according to provided policies 30 | product = submergers: l: 31 | let 32 | folded-attrs = pkgs.lib.foldAttrs (n: a: [n] ++ a) [] l; 33 | outcome = pkgs.lib.mapAttrs (n: submerger: 34 | submerger (folded-attrs.${n} or []) 35 | ) submergers; 36 | result = pkgs.lib.mapAttrs (n: v: v.result) outcome; 37 | errors = builtins.concatLists (builtins.map (n: 38 | builtins.map (e: e // { path = [n] ++ e.path; }) outcome.${n}.errors 39 | ) (builtins.attrNames result)); 40 | in 41 | if errors != [] then { inherit errors; } 42 | else { inherit result errors; }; 43 | in 44 | { 45 | merge = name: merger: value: 46 | let 47 | merged = merger value; 48 | res = merged.result; 49 | assertions = builtins.map (e: { 50 | meta.type = "assertion-failure"; 51 | message = 52 | builtins.concatStringsSep "." ([name] ++ e.path) + ": " + e.error; 53 | }) merged.errors; 54 | in 55 | if assertions != [] then { inherit assertions; } 56 | else { inherit res assertions; }; 57 | 58 | inherit const product; 59 | 60 | attrs = { 61 | disjoint-union = attrs-disjoint-union; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /nixtos/lib/solve-block-devices/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | block-devices: 4 | 5 | let 6 | build-one = bd-name: '' 7 | if [ ! -b "${bd-name}" ]; then 8 | echo "Building ${bd-name}" 9 | ${block-devices.${bd-name}.build-command bd-name} 10 | fi 11 | ''; 12 | 13 | wait-for = bd-name: '' 14 | while [ ! -b ${bd-name} ]; do 15 | sleep 0 16 | done 17 | ''; 18 | 19 | # TODO(low): it would likely be a bit better to wait for a device only just 20 | # before it is required, and not immediately after building it, for better 21 | # parallelization 22 | build-and-wait-for = bd-name: 23 | pkgs.lib.concatStringsSep "\n" (map (bd-name: 24 | build-one bd-name + wait-for bd-name 25 | ) ( 26 | top.lib.sorted-deps-of (a: b: # does a depend on b? 27 | builtins.elem b block-devices.${a}.depends-on 28 | ) (builtins.attrNames block-devices) [ bd-name ] 29 | )); 30 | in 31 | { 32 | inherit build-and-wait-for; 33 | } 34 | -------------------------------------------------------------------------------- /nixtos/lib/solve-filesystems/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | filesystems: 4 | 5 | # TODO(high) Add a way to override automated dependency management, eg. for a 6 | # root overlayfs that would use mounts “below” it. 7 | let 8 | # List of the mount points 9 | mountpoints = builtins.attrNames filesystems; 10 | 11 | # Returns all parent directories (including the path itself) of a path. 12 | all-parents = path: 13 | if path == "/" then [ "/" ] 14 | else all-parents (dirOf path) ++ [ path ]; 15 | 16 | # Returns all paths that must be present in order to mount a filesystem. 17 | # This only gives “leaf” paths. 18 | all-leaf-deps = mount: filesystems.${mount}.wait-for-files ++ 19 | (if (mount != "/") then [ (dirOf mount) ] else []); 20 | 21 | # Returns all paths that must be present in order to mount a filesystem, 22 | # including parent paths. 23 | all-path-deps = mount: pkgs.lib.flatten ( 24 | map all-parents (all-leaf-deps mount) 25 | ); 26 | 27 | # Returns true iff `b` must be mounted before `a` (ie. `a` depends on `b`) 28 | depends-on = a: b: builtins.elem b (all-path-deps a); 29 | 30 | # Returns all the parent mount points of a path (including itself). 31 | all-parent-mount-points = path: 32 | builtins.filter (x: builtins.elem x mountpoints) 33 | (all-parents path); 34 | 35 | # TODO(high): Actually add some wait to check the wait-for-files files are 36 | # actually present before mounting 37 | # Returns a script that mounts the passed filesystems in the order given 38 | mount-fs-list = mountpoints: root: 39 | pkgs.lib.concatStringsSep "\n" (map (fs: 40 | "mkdir -p ${root}${fs}\n" + 41 | filesystems.${fs}.mount-command root fs 42 | ) mountpoints); 43 | 44 | # The (ordered) list of all filesystems 45 | all-filesystems = top.lib.sorted-deps-of depends-on mountpoints mountpoints; 46 | 47 | # Returns a script that mounts all filesystems under `root` 48 | mount-all = root: mount-fs-list all-filesystems root; 49 | 50 | # Returns the (ordered) list of filesystems that have to be mounted in order 51 | # to access `path` 52 | filesystems-for = path: 53 | top.lib.sorted-deps-of depends-on mountpoints ( 54 | all-parent-mount-points path 55 | ); 56 | 57 | # Returns a script that mounts all the filesystems required in order to access 58 | # `path` 59 | mount-filesystems-for = path: root: mount-fs-list (filesystems-for path) root; 60 | 61 | # TODO(low): Also handle the case where the user was crazy enough to have a 62 | # mount point *below* the nix store? 63 | initrd-block-devices = 64 | pkgs.lib.flatten (map (x: filesystems.${x}.wait-for-block-devices) 65 | (filesystems-for "/nix/store")); 66 | in 67 | { 68 | inherit initrd-block-devices mount-all mount-filesystems-for; 69 | } 70 | -------------------------------------------------------------------------------- /nixtos/lib/solve-services/default.nix: -------------------------------------------------------------------------------- 1 | # This file defines the service solver for NixtOS. 2 | # 3 | # Here, a service is defined as a function that takes as input a list of 4 | # extending blocks, and outputs a map of service names to extending blocks. 5 | # The returned map will add extending blocks to the extending block list of the 6 | # named services. The map values can be either a set, in which case it is 7 | # directly the extending block, or a list, in which case it is a list of 8 | # extending blocks that will be passed on to the extended service. 9 | # 10 | # An extending block, also called extender, is defined as a map of the format: 11 | # { meta.type = "..."; # The type of the data, as a string 12 | # ... # The data contained 13 | # } 14 | # 15 | # The "meta.type" argument can be one of the following: 16 | # * "init": Init service, with a `command` argument 17 | # * "script": Script, with a `script` argument 18 | # * "symlink": Symlink, with `file` and `target` arguments 19 | # * "service": Service, with `name` and `script` arguments 20 | # * TODO(medium): this should be auto-generated from doc in each module 21 | # * Anything with a ':' in it, which is defined and used outside of NixtOS. A 22 | # user should prefix his types with 'user:' for personal use, and services 23 | # distributed for further use should be prefixed with 'domain.example.org:' 24 | # 25 | # `meta` is further reserved for future NixtOS usage. All other fields can be 26 | # user-defined. 27 | # 28 | # When receiving an extender, `meta.source` will be set to the name of the 29 | # service that generated said extender. 30 | 31 | # TODO(high): think about reverse-dependencies. Current idea: 32 | # rec { 33 | # logger = logger { ... }; 34 | # foo = foo { logger = logger.interface; ... }; 35 | # } 36 | # Issue: HEAVY! don't want to pass the logger everywhere. 37 | # Other idea: make it a two-step fix-point, first propagating 38 | # reverse-dependencies, then extenders. 39 | 40 | # TODO(high): simplify handling of extenders for extended modules 41 | 42 | # TODO(low): consider toposorting instead of fixpoint, for error msg & speed? 43 | 44 | # TODO(medium): add a callPackage equivalent to avoid writing `dbus = "dbus"` 45 | 46 | # TODO(low): consider adding lazy modules 47 | # The point is to avoid the need for the user to have lines like 48 | # dbus = services.dbus {}; 49 | # in their configuration. The idea, for implementation, would be to split the 50 | # “lazy” service set from the user-defined service set, and to use the 51 | # callPackage-like (defined from the to-do item above) to automatically detect 52 | # which dependencies are required. 53 | # Then first do a DFS through the lazy//user package set to identify which 54 | # dependencies are required, and second do the service solving from this service 55 | # set. 56 | # This is to be done iff some people express real-life annoyance at being forced 57 | # to write explicitly all dependencies. But we must be ready to make this change 58 | # at any point in time, without breaking anything else. 59 | 60 | ######################################################################## 61 | # # 62 | # YOU WHO ENTER HERE WISHING TO MODIFY STUFF # 63 | # # 64 | # READ THE ABOVE TO-DO ITEMS AND MAKE SURE NOT TO MAKE THEM IMPOSSIBLE # 65 | # # 66 | # IMPLEMENTATION SIMPLICITY IS A STRENGTH # 67 | # # 68 | ######################################################################## 69 | 70 | { pkgs, top }: 71 | 72 | # services: map service-name service 73 | # where service = list extending-block -> map service-name service-extension 74 | # where service-extension = extending-block | list extending-block 75 | services: 76 | 77 | let 78 | # sanitize-extension: service-name -> service-extension 79 | # -> list extending-block 80 | # 81 | # Adds the `meta.source` attribute (and wraps in a list if need be) 82 | sanitize-extension = source: ext: 83 | builtins.map (e: 84 | e // { meta = e.meta // { inherit source; }; } 85 | ) ( 86 | if builtins.isList ext then ext 87 | else [ ext ] 88 | ); 89 | 90 | # all-extenders-from-service: service-name -> service 91 | # -> map service-name (list extending-block) 92 | # 93 | # All the extenders that one service generates, sanitized 94 | all-extenders-from-service = source: service: 95 | pkgs.lib.mapAttrs (_: ext: 96 | sanitize-extension source ext 97 | ) (service (extenders-for source)); 98 | 99 | # all-extenders: map service-name (list extending-block) 100 | # 101 | # All the extenders, grouped by extended service 102 | all-extenders = pkgs.lib.foldAttrs (n: a: n ++ a) [] ( 103 | pkgs.lib.mapAttrsToList all-extenders-from-service services 104 | ); 105 | 106 | # extenders-for: service-name -> list extending-block 107 | # 108 | # List of all the extenders that target service `service` 109 | extenders-for = service: all-extenders.${service} or []; 110 | 111 | # TODO(medium) This should be moved to the simplified handling of extenders 112 | extenders-for-assert-type = service: type: 113 | map (e: assert e.meta.type == type; e) (extenders-for service); 114 | in 115 | { 116 | inherit all-extenders extenders-for extenders-for-assert-type; 117 | } 118 | -------------------------------------------------------------------------------- /nixtos/lib/types/default.nix: -------------------------------------------------------------------------------- 1 | # A simple type system to check plain nix values 2 | # and get detailed error messages on type mismatch. 3 | # 4 | # Contains support for 5 | # scalars (simple values) 6 | # recursive types (lists of t and attrs of t) 7 | # products (attribute sets with named fields) 8 | # sums (tagged unions where you can match on the different cases) 9 | # unions (untagged unions of the specified types) 10 | # We can’t type functions (lambda expressions). Maybe in the future. 11 | # 12 | # What is the difference to `./types.nix`? / Why another type system? 13 | # 14 | # `./types.nix` is deeply entangled with the module system, 15 | # in order to use it on plain nix values, you have to invoke the 16 | # module system, which is pretty heavyweight and hard/verbose to do. 17 | # Contrary to popular opinion, the `check` functions on module types 18 | # does *not* do a recursive check for complex types/values. 19 | # Plus, it is not possible to catch a type error, since the module 20 | # system always instantly aborts nix evaluation on type error. 21 | # The `check-type` function in this module returns a detailed, 22 | # structured # error for each part of the substructure that 23 | # does not match the given expected type. 24 | # Concerning expressibility, an attrset with fixed fields can 25 | # be given as easy as `product { field1 = type; … }`, whereas 26 | # in `./types.nix` you need to use the complictated `submodule` 27 | # mechanism. We also support tagged unions (`./types.nix` does not) 28 | # and untagged unions of an arbitrary set of types (can be emulated 29 | # with nested `either`s in `./types.nix`). 30 | # 31 | # In short: if you want to check a module option, use `./types.nix`. 32 | # If you want to check a plain (possibly complex) nix value, 33 | # use this module. 34 | # 35 | # The main function is `check-type`. 36 | # Tests can be found in './tests/types-simple.nix`. 37 | 38 | { pkgs, top }: 39 | 40 | let 41 | lib = pkgs.lib; 42 | 43 | # The type functor. 44 | # t is the recursion “not yet inserted”. 45 | # 46 | # data Type t 47 | # = Scalar 48 | # | Recursive (Rec t) 49 | # | Sum (Map String t) 50 | # | Product (Map String t) 51 | # | Union (List t) 52 | # deriving (Functor) 53 | # 54 | # Fix Type is every t replaced with Type, recursively. 55 | 56 | # The alternatives above are tagged manually, by this variant enum: 57 | variants = { 58 | scalar = 0; 59 | recursive = 1; 60 | sum = 2; 61 | product = 3; 62 | union = 4; 63 | }; 64 | 65 | ## -- HELPERS -- 66 | 67 | unreachable = abort "should not be reached"; 68 | 69 | # Functor instance of Type 70 | # fmap :: (a -> b) -> (Type a) -> (Type b) 71 | # it just applies a function over the “holes” in Type variants 72 | fmap = f: t: 73 | if t.variant == variants.scalar then t 74 | else if t.variant == variants.recursive then 75 | t // { nested = f t.nested; } 76 | else if t.variant == variants.sum then 77 | t // { alts = lib.mapAttrs (lib.const f) t.alts; } 78 | else if t.variant == variants.product then 79 | t // { opt = lib.mapAttrs (lib.const f) t.opt; 80 | req = lib.mapAttrs (lib.const f) t.req; } 81 | else if t.variant == variants.union then 82 | t // { altList = map f t.altList; } 83 | else unreachable; 84 | 85 | # cata :: (Type a -> a) -> Fix Type -> a 86 | # collapses the structure Fix Type (nested types) into an a, 87 | # by collapsing one layer at a time with the function (/algebra) 88 | # alg :: (Type a -> a) 89 | cata = alg: t: alg (fmap (cata alg) t); 90 | 91 | 92 | ## -- MAIN -- 93 | 94 | # Main type checking function. 95 | # Example: 96 | # > check-type (list string) [ "foo" "bar" ] 97 | # { } 98 | # > check-type (list string) [ "foo" 42 ] 99 | # { "1" = { should = "string"; val = 42; }; } 100 | # 101 | # check-type :: Fix Type -> Value -> (Nested Attrs) Errors 102 | # 103 | # where { } means no error (the given value is of the given type) 104 | # and { should : String, val : Value } denotes a type mismatch. 105 | check-type = 106 | let 107 | # the type check suceeded 108 | ok = {}; 109 | # filters out non-error messages 110 | mapAndFilter = f: vals: 111 | lib.filterAttrs (_: v: v != {}) (lib.mapAttrs f vals); 112 | # alg :: Type (Value -> Errors) -> (Value -> Errors) 113 | alg = t: v: 114 | # the main type check on each “level” 115 | # the cases further down handle the differences 116 | # between the variants (poor man’s pattern matching) 117 | # TODO: some errors should throw some more context. 118 | # e.g. putting more than one field in a sum value 119 | if !(t.check v) then { should = t.description; val = v; } 120 | # scalars have just one level (already checked above) 121 | else if t.variant == variants.scalar then ok 122 | # grab all child values and type check them one by one 123 | else if t.variant == variants.recursive then 124 | mapAndFilter (_: el: t.nested el) (t.each v) 125 | # there’s exactly one tagged value, so check that 126 | else if t.variant == variants.sum then 127 | # we already tested length == 1 in .check 128 | let alt = builtins.head (builtins.attrNames v); 129 | in t.alts.${alt} v.${alt} 130 | # check each field according to its type 131 | # optional missing fields of course always pass the check 132 | else if t.variant == variants.product then 133 | mapAndFilter (n: f: if v ? ${n} then f v.${n} else ok) 134 | (t.req // t.opt) 135 | # if the value fails the check for each type it can have, 136 | # we throw an error; if one check succeeds the union is satisfied 137 | else if t.variant == variants.union then 138 | # unions are awkward, the type checker can’t do much here 139 | if lib.all (res: res != ok) (map (f: f v) t.altList) 140 | then { should = t.description; val = v; } 141 | else ok 142 | else unreachable; 143 | # cata only has “two arguments”, giving it a Value as third 144 | # argument “morphs” the `a` in alg to (Value -> Errors); 145 | # of course we could curry t and v away, 146 | # but just `cata alg` would be very confusing ;) 147 | in t: v: cata alg t v; 148 | 149 | # assert-type: assert that the type of `value` is `type`, complaining as 150 | # `name` otherwise 151 | assert-type = name: value: type: 152 | let res = check-type type value; in 153 | if res == {} then [] 154 | else [ { 155 | meta.type = "assertion-failure"; 156 | message = prettyPrintErrors name res; 157 | } ]; 158 | 159 | 160 | ## -- TYPE SETUP STUFF -- 161 | 162 | mkBaseType = { 163 | # the (displayable) type description 164 | description, 165 | # a function to check the outermost type, given a value (Val -> Bool) 166 | check, 167 | # the variant of this type 168 | variant, 169 | # extra fields belonging to the variant 170 | extraFields 171 | }: { inherit description check variant; } // extraFields; 172 | 173 | mkScalar = { description, check }: mkBaseType { 174 | inherit description check; 175 | variant = variants.scalar; 176 | extraFields = {}; 177 | }; 178 | 179 | mkRecursive = { description, check, 180 | # return all children for a value of this type T t, 181 | # give each child (of type t) a displayable name. 182 | # (T t -> Map Name t) 183 | each, 184 | # The nested value t of the type functor 185 | nested 186 | }: mkBaseType { 187 | inherit description check; 188 | variant = variants.recursive; 189 | extraFields = { inherit each nested; }; 190 | }; 191 | 192 | 193 | ## -- TYPES -- 194 | 195 | # the type with no inhabitants (kind of useless …) 196 | void = mkScalar { 197 | description = "void"; 198 | # there are no values of type void 199 | check = lib.const false; 200 | }; 201 | 202 | # the any type, every value is an inhabitant 203 | # it basically turns off the type system, use with care 204 | any = mkScalar { 205 | description = "any type"; 206 | check = lib.const true; 207 | }; 208 | 209 | # the type with exactly one inhabitant 210 | unit = mkScalar { 211 | description = "unit"; 212 | # there is exactly one unit value, we represent it with {} 213 | # Q: why not `null`? 214 | # A: `null` has strong connotations as the “always existing” 215 | # alternative value; of course in a unityped language like 216 | # nix this is moot, but here we take the chance to throw out 217 | # this harmful idea (the “million dollar mistake”). 218 | check = v: v == {}; 219 | }; 220 | 221 | const = value: mkScalar { 222 | description = "const ${lib.generators.toPretty {} value}"; 223 | check = v: v == value; 224 | }; 225 | 226 | # the type with two inhabitants 227 | bool = mkScalar { 228 | description = "boolean"; 229 | check = builtins.isBool; 230 | }; 231 | 232 | # a nix string 233 | string = mkScalar { 234 | description = "string"; 235 | check = builtins.isString; 236 | }; 237 | 238 | # a nix path 239 | path = mkScalar { 240 | description = "path"; 241 | # there is no `isPath` predicate, 242 | # but `typeOf` exists since 1.6.1 243 | check = v: builtins.typeOf v == "path"; 244 | }; 245 | 246 | # a signed nix integer 247 | int = mkScalar { 248 | description = "integer"; 249 | check = builtins.isInt; 250 | }; 251 | 252 | # a nix floating point number 253 | float = mkScalar { 254 | description = "float"; 255 | check = builtins.isFloat; 256 | }; 257 | 258 | # helper for descriptions of recursive types 259 | # TODO: descriptions need to assume t is a type, 260 | # which is only true for Fix Type. How to make nice? 261 | describe = t: t.description or ""; 262 | 263 | # list with children of type t 264 | # list bool: 265 | # [ true false false ] 266 | # list (attrs unit): 267 | # [ { a = {}; } { b = {}; } ] 268 | # [] 269 | list = t: mkRecursive { 270 | description = "list of ${describe t}"; 271 | check = builtins.isList; 272 | # each child gets named by its index, starting from 0 273 | each = l: builtins.listToAttrs 274 | (lib.imap0 (i: v: lib.nameValuePair (toString i) v) l); 275 | nested = t; 276 | }; 277 | 278 | # attrset with children of type t 279 | # attrs int: { foo = 23; bar = 42; } 280 | # attrs (attrs string): 281 | # { foo.bar = "hello"; baz.quux = "x"; } 282 | # { x = { y = "wow"; }; } 283 | attrs = t: mkRecursive { 284 | description = "attrset of ${describe t}"; 285 | check = builtins.isAttrs; 286 | each = lib.id; 287 | nested = t; 288 | }; 289 | 290 | # TODO: nonempty list and attrs 291 | 292 | # product type with fields of the specified types 293 | # product { x = int; y = unit; }: 294 | # { x = 23; y = {}; } 295 | # { x = 42; y = {}; } 296 | # product {}: <- yeah, that’s isomorphic to unit 297 | # { } 298 | # product { foo = void; }: 299 | # just kidding. :) 300 | product = fields: product-opt { req = fields; opt = {}; }; 301 | 302 | # product type with the possibility of optional fields 303 | # actually the more generic type of product, BUT: 304 | # code with a fixed number of fields is less brittle. 305 | # choose wisely. 306 | # product-opt { req = {}; opt = { a = unit; b = int; }: 307 | # { } 308 | # { a = {}; } 309 | # { a = {}; b = 23; } 310 | # if a product is `open`, any fields that are not 311 | # given a type in either `req` or `opt` will default 312 | # to type `any` (that is they typecheck by default). 313 | # product-opt { req = { a = int; }; opt = {}; open = true; } 314 | # { a = 23; } 315 | # { a = 42; b = "foo"; c = false; } 316 | product-opt = { req, opt, open ? false }: 317 | let reqfs = builtins.attrNames req; 318 | optfs = builtins.attrNames opt; in 319 | # opt and rec fields must not contain the same fields 320 | assert (lib.intersectLists reqfs optfs == []); 321 | mkBaseType { 322 | description = "{ " + 323 | lib.concatStringsSep ", " 324 | ( lib.mapAttrsToList (n: t: "${n}: ${describe t}") req 325 | ++ lib.mapAttrsToList (n: t: "[${n}: ${describe t}]") opt 326 | # TODO: maybe but this at the beginning: [ …, 327 | # so that it’s easier to see that an attrset is open 328 | ++ lib.optional open "…") 329 | + " }"; 330 | check = v: 331 | let vfs = builtins.attrNames v; in 332 | lib.foldl lib.and (builtins.isAttrs v) [ 333 | # if there’s only required fields, this is an optimization 334 | (opt == {} && !open -> reqfs == vfs) 335 | # all required fields have to exist in the value 336 | # reqfs - vfs 337 | (lib.subtractLists vfs reqfs == []) 338 | # whithout req, and if the product is not open 339 | # only opt fields must be in the value 340 | # (vfs - reqfs) - otfs 341 | (!open -> [] == lib.subtractLists optfs 342 | (lib.subtractLists reqfs vfs)) 343 | ]; 344 | variant = variants.product; 345 | extraFields = { 346 | inherit opt req open; 347 | }; 348 | }; 349 | 350 | # sum type with alternatives of the specified types 351 | # sum { left = string; right = bool; }: 352 | # { left = "work it"; } 353 | # { right = false; } 354 | # sum { true = unit; false = unit; } <- that’s isomorphic to bool 355 | # { true = {}; } 356 | # { false = {}; } 357 | # sum { X = product { name = string; age = int; }; Y = list unit; } 358 | # { X = { name = "peter shaw"; age = 22; }; } 359 | # { Y = [ {} {} {} {} {} {} {} {} ]; } 360 | sum = alts: assert alts != {}; mkBaseType { 361 | description = "< " + 362 | lib.concatStringsSep " | " 363 | (lib.mapAttrsToList (n: t: "${n}: ${describe t}") alts) 364 | + " >"; 365 | check = v: 366 | let alt = builtins.attrNames v; 367 | in builtins.isAttrs v 368 | # exactly one of the alts has to be used by the value 369 | && builtins.length alt == 1 370 | # the alt tag of the value should of course be a possibility 371 | && alts ? ${lib.head alt}; 372 | variant = variants.sum; 373 | extraFields = { 374 | inherit alts; 375 | }; 376 | }; 377 | 378 | # untagged union type 379 | # ATTENTION: this leads to *bad* type checker errors in practice, 380 | # you also can’t do pattern matching; use sum if possible. 381 | # union [ bool int ] 382 | # 3 383 | # true 384 | # list (union [ int string ]) 385 | # [ "foo" 34 "bar" ] 386 | # please don’t use this. 387 | union = altList: assert altList != []; mkBaseType { 388 | description = "one of [ " 389 | + lib.concatMapStringsSep ", " describe altList 390 | + " ]"; 391 | # any type that checks out is fine 392 | check = v: lib.any (t: t.check v) altList; 393 | variant = variants.union; 394 | extraFields = { 395 | inherit altList; 396 | }; 397 | }; 398 | 399 | # restrict applies a further check to values of type 400 | # the idea is simple, but some crazy things are possible, like 401 | # * even integers 402 | # * integers between 23 and 42 403 | # * enumerations 404 | # * lists with exactly three elements where the second is the string "bla" 405 | # type errors from the base type checks are retained. 406 | # 407 | # restrict { type = int; check = isEven; … }: 408 | # 2 409 | # 42 410 | # see tests for further examples 411 | restrict = { 412 | # type that should be restricted 413 | type, 414 | # takes a value of type 415 | # return true for values of type that are valid 416 | check, 417 | # the (displayable) restricted type description 418 | description 419 | }: type // { 420 | inherit description; 421 | # first the general type is checked, 422 | # then the restriction check is tried 423 | # this way the restriction check can assume the correct type 424 | check = v: type.check v && check v; 425 | }; 426 | 427 | # TODO: should scalars be allowed as nest types? 428 | # TODO: how to implement? 429 | # nested = nest: t: mkBaseType { 430 | # description = "nested ${describe nest} of ${describe t}"; 431 | # check = nest.check ; 432 | # variant = nest.variant; 433 | # extraFields = { 434 | 435 | 436 | ## -- FUNCTIONS -- 437 | 438 | # TODO: pattern match function 439 | # match = 440 | 441 | # Feed it the output of check-type (after testing for success (== {}) 442 | # and it returns a more or less pretty string of errors. 443 | prettyPrintErrors = 444 | let 445 | isLeaf = v: {} == check-type (product { should = string; val = any; }) v; 446 | recurse = path: errs: 447 | if isLeaf errs 448 | then [{ inherit path; inherit (errs) should val; }] 449 | else builtins.concatLists (lib.mapAttrsToList 450 | (p: errs': recurse (path ++ [p]) errs') errs); 451 | pretty = { path, should, val }: 452 | "${lib.concatStringsSep "." path} should be: ${ 453 | should}\nbut is: ${lib.generators.toPretty {} val}"; 454 | in name: errs: lib.concatMapStringsSep "\n" pretty (recurse [name] errs); 455 | 456 | in { 457 | # The type of nix types, as non-recursive functor. 458 | # fmap and cata are specialized to Type. 459 | Type = { inherit variants fmap cata; }; 460 | # Constructor functions for types. 461 | # Their internal structure/fields are an *implementation detail*. 462 | inherit void any unit const bool string path int 463 | float list attrs product product-opt sum 464 | union restrict; 465 | # Type checking. 466 | inherit check-type assert-type; 467 | # Functions. 468 | inherit prettyPrintErrors; 469 | } 470 | -------------------------------------------------------------------------------- /nixtos/operating-system/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | { 3 | name ? top.version.name, 4 | kernel ? pkgs.linuxPackages.kernel, 5 | initrd-modules ? [], 6 | block-devices, 7 | filesystems, 8 | packages, 9 | services ? {}, 10 | }: 11 | 12 | assert !(services ? "kernel"); 13 | assert !(services ? "activation-scripts"); 14 | # TODO(high): Add de-activation scripts 15 | # The idea is to be able to delete state directories that are no longer needed. 16 | # So the de-activation scripts would be run and passed as arguments the new 17 | # config, so that they can remove things no longer wanted 18 | # NOTE: this MUST NOT delete any user data, ONLY things that can be re-generated 19 | # from the configuration, should a roll-back occur 20 | # Maybe it would make sense to warn the user about state directories used by no 21 | # service too? 22 | 23 | let 24 | solved-services = top.lib.solve-services services; 25 | 26 | assertion-extenders = 27 | solved-services.extenders-for-assert-type "assertions" "assertion-failure"; 28 | assert-assertions = 29 | if assertion-extenders == [] then {} 30 | else throw '' 31 | Assertions failed: 32 | 33 | ${builtins.concatStringsSep "\n" ( 34 | builtins.map (a: 35 | " * In service '${a.meta.source}':\n " + 36 | builtins.replaceStrings ["\n"] ["\n "] a.message 37 | ) assertion-extenders 38 | )} 39 | ''; 40 | 41 | kernel-extenders = solved-services.extenders-for-assert-type "kernel" "init"; 42 | init-command = assert builtins.length kernel-extenders == 1; 43 | (builtins.head kernel-extenders).command; 44 | 45 | activation-extenders = 46 | solved-services.extenders-for-assert-type "activation-scripts" "script"; 47 | activation-script = pkgs.writeScript "activation-script" '' 48 | #!${pkgs.bash}/bin/bash 49 | PATH=${pkgs.coreutils}/bin 50 | 51 | ${builtins.concatStringsSep "\n" (map (e: e.script) activation-extenders)} 52 | ''; 53 | 54 | initrd = top.lib.make-initrd { 55 | inherit kernel; 56 | 57 | modules = pkgs.lib.unique ( 58 | initrd-modules ++ 59 | pkgs.lib.flatten (pkgs.lib.mapAttrsToList (device: device-type: 60 | device-type.extra-modules 61 | ) block-devices) ++ 62 | pkgs.lib.flatten (pkgs.lib.mapAttrsToList (fs-name: fs-type: 63 | fs-type.extra-modules 64 | ) filesystems) 65 | ); 66 | 67 | inherit block-devices filesystems; 68 | }; 69 | 70 | modules = pkgs.aggregateModules [ kernel ]; 71 | 72 | system-packages = pkgs.buildEnv { 73 | name = "system-packages"; 74 | paths = packages; 75 | ignoreCollisions = true; 76 | }; 77 | 78 | complete-system = pkgs.stdenvNoCC.mkDerivation { 79 | inherit name; 80 | 81 | buildCommand = '' 82 | mkdir $out 83 | 84 | ln -s ${kernel}/bzImage $out/kernel 85 | ln -s ${initrd}/initrd $out/initrd 86 | ln -s ${modules} $out/kernel-modules 87 | ln -s ${system-packages} $out/sw 88 | 89 | cat > $out/init < /proc/sys/kernel/modprobe 112 | 113 | ${activation-script} 114 | 115 | exec ${init-command} 116 | EOF 117 | chmod +x $out/init 118 | ''; 119 | 120 | passthru = { 121 | inherit solved-services; 122 | }; 123 | }; 124 | in 125 | builtins.seq assert-assertions complete-system 126 | -------------------------------------------------------------------------------- /nixtos/pam/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | __functor = self: import ./service.nix { inherit pkgs top; }; 5 | env = import ./env.nix { inherit pkgs top; }; 6 | } 7 | -------------------------------------------------------------------------------- /nixtos/pam/env.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | assertions ? "assertions", 5 | pam ? "pam", 6 | vars, 7 | } @ args: 8 | 9 | extenders: # Ignored 10 | 11 | { 12 | ${assertions} = with top.lib.types; 13 | assert-type "nixtos.pam.env's argument" args (product-opt { 14 | req = { 15 | vars = attrs string; 16 | }; 17 | opt = { 18 | assertions = string; 19 | pam = string; 20 | }; 21 | }); 22 | 23 | ${pam} = pkgs.lib.mapAttrsToList (name: value: { 24 | meta.type = "env"; 25 | inherit name value; 26 | }) vars; 27 | } 28 | -------------------------------------------------------------------------------- /nixtos/pam/service.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | assertions ? "assertions", 5 | files ? "files", 6 | config ? {}, # TODO(medium): pass extender-collected config here 7 | } @ args: 8 | 9 | extenders: # TODO(medium): Allow adding PAM configuration via extenders 10 | 11 | let 12 | asserts = with top.lib.types; 13 | assert-type "nixtos.pam's argument" args (product-opt { 14 | req = {}; 15 | opt = { 16 | assertions = string; 17 | files = string; 18 | config = attrs string; 19 | }; 20 | }); 21 | 22 | env = top.lib.make-attrset (e: 23 | throw "Trying to define the same session environment variable at multiple positions: ${builtins.toJSON e}" 24 | ) (builtins.map (e: 25 | { name = e.name; value = e; } 26 | ) (builtins.filter (e: 27 | e.meta.type == "env" 28 | ) extenders)); 29 | 30 | env-file = pkgs.writeText "pam-env" ( 31 | builtins.concatStringsSep "\n" (pkgs.lib.mapAttrsToList (var: d: 32 | "${var}=${d.value}" 33 | ) env) 34 | ); 35 | 36 | cfg = { 37 | other = '' 38 | auth required pam_warn.so 39 | auth requisite pam_deny.so 40 | 41 | account required pam_warn.so 42 | account requisite pam_deny.so 43 | 44 | password required pam_warn.so 45 | password requisite pam_deny.so 46 | 47 | session required pam_deny.so 48 | session requisite pam_warn.so 49 | ''; 50 | 51 | login = '' 52 | account sufficient pam_unix.so 53 | 54 | auth sufficient pam_unix.so 55 | auth requisite pam_deny.so 56 | 57 | password requisite pam_unix.so sha512 58 | 59 | session required pam_env.so envfile=${env-file} 60 | session required pam_unix.so 61 | session required pam_loginuid.so 62 | session required pam_lastlog.so 63 | ''; 64 | } // config; 65 | in 66 | { 67 | ${assertions} = asserts; 68 | 69 | ${files} = pkgs.lib.mapAttrsToList (service: conf: 70 | { meta.type = "symlink"; 71 | file = "/etc/pam.d/${service}"; 72 | target = pkgs.writeScript "pam-${service}" conf; 73 | } 74 | ) cfg; 75 | } 76 | -------------------------------------------------------------------------------- /nixtos/service-graph/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | { name ? "service-graph", solved-services }: 3 | 4 | let 5 | s = solved-services; 6 | services = builtins.attrNames s.all-extenders; 7 | in 8 | pkgs.writeTextFile { 9 | name = "${name}.dot"; 10 | text = '' 11 | digraph "${name}" { ${ 12 | builtins.concatStringsSep "" (map (to: 13 | builtins.concatStringsSep "" (map (e: '' 14 | "${e.meta.source}" -> "${to}"; 15 | '') (s.extenders-for to)) 16 | ) services) 17 | } } 18 | ''; 19 | } 20 | -------------------------------------------------------------------------------- /nixtos/tty/agetty/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | init ? "init", 5 | try-to-keep-baud ? true, 6 | baud-rates ? [ 115200 38400 9600 ], 7 | }: 8 | 9 | tty: 10 | 11 | extenders: 12 | 13 | { 14 | ${init} = { 15 | meta.type = "service"; 16 | name = "agetty-${tty}"; 17 | script = '' 18 | #!${pkgs.bash}/bin/bash 19 | 20 | exec ${pkgs.utillinux}/bin/agetty \ 21 | --login-program ${pkgs.shadow}/bin/login \ 22 | --noclear \ 23 | ${pkgs.lib.optionalString try-to-keep-baud "--keep-baud"} \ 24 | ${tty} \ 25 | ${pkgs.lib.concatMapStringsSep "," toString baud-rates} 26 | ''; 27 | # TODO(medium): should log under ‘log’ user… anyway this should not be 28 | # tied to runit. 29 | log-script = '' 30 | #!${pkgs.bash}/bin/bash 31 | 32 | ${pkgs.coreutils}/bin/mkdir /var/log/agetty-${tty} 33 | exec ${pkgs.runit}/bin/svlogd -tt /var/log/agetty-${tty} 34 | ''; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /nixtos/tty/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | agetty = import ./agetty { inherit pkgs top; }; 5 | } 6 | -------------------------------------------------------------------------------- /nixtos/udev/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | eudev = import ./eudev { inherit pkgs top; }; 5 | } 6 | -------------------------------------------------------------------------------- /nixtos/udev/eudev/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | init ? "init", 5 | files ? "files", 6 | extra-packages ? [], 7 | extra-path ? [], 8 | }: 9 | 10 | _: # TODO(medium): Allow adding udev rules via extenders 11 | 12 | let 13 | packages = [ pkgs.eudev ] ++ extra-packages; 14 | 15 | path = with pkgs; [ coreutils gnused gnugrep utillinux eudev ]; 16 | 17 | hwdb = pkgs.runCommand "hwdb.bin" {} '' 18 | echo "Building link farm..." 19 | mkdir -p etc/udev/hwdb.d 20 | ${pkgs.lib.concatMapStringsSep "\n" (x: '' 21 | for i in ${x}/{etc,var/lib}/udev/hwdb.d/*; do 22 | ln -s "$i" etc/udev/hwdb.d/"$(basename "$i")" 23 | done 24 | '') packages} 25 | 26 | echo "Generating database..." 27 | ! ${pkgs.eudev}/bin/udevadm hwdb --update --root="$(pwd)" 2>&1 | grep Error 28 | 29 | echo "Everything went well" 30 | mv etc/udev/hwdb.bin $out 31 | ''; 32 | 33 | rules = pkgs.runCommand "rules.d" {} '' 34 | ln -s ${pkgs.eudev}/var/lib/udev/rules.d $out 35 | ''; 36 | in 37 | 38 | { 39 | ${init} = { meta.type = "service"; 40 | name = "udev"; 41 | script = '' 42 | #!${pkgs.bash}/bin/bash 43 | 44 | ${pkgs.eudev}/bin/udevd --debug 2>&1 & 45 | pid="$!" 46 | 47 | ${pkgs.eudev}/bin/udevadm trigger -c add 48 | ${pkgs.eudev}/bin/udevadm trigger 49 | ${pkgs.eudev}/bin/udevadm settle 50 | 51 | wait "$pid" 52 | ''; 53 | # TODO(medium): should log under ‘log’ user… anyway this should not be 54 | # tied to runit. 55 | log-script = '' 56 | #!${pkgs.bash}/bin/bash 57 | 58 | ${pkgs.coreutils}/bin/mkdir /var/log/eudev 59 | exec ${pkgs.runit}/bin/svlogd -tt /var/log/eudev 60 | ''; 61 | }; 62 | 63 | ${files} = [ 64 | { meta.type = "symlink"; 65 | file = "/etc/udev/hwdb.bin"; 66 | target = hwdb; 67 | } 68 | { meta.type = "symlink"; 69 | file = "/etc/udev/rules.d"; 70 | target = rules; 71 | } 72 | ]; 73 | } 74 | -------------------------------------------------------------------------------- /nixtos/users/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | unix = import ./unix { inherit pkgs top; }; 5 | } 6 | -------------------------------------------------------------------------------- /nixtos/users/unix/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | rec { 4 | # Configuration helpers 5 | # ===================== 6 | 7 | # users: ({ name:string, users:list user } | list user) -> service 8 | # TODO(low): typecheck? 9 | users = arg: # TODO(low): abstract this away from unix? will be hard 10 | if builtins.isList arg then users { name = "users"; users = arg; } 11 | else 12 | _ignored_extenders: 13 | { 14 | ${arg.name} = 15 | builtins.map (user: user // { meta.type = "user"; }) arg.users; 16 | }; 17 | 18 | # Main implementation functor 19 | # =========================== 20 | 21 | __functor = self: { 22 | files ? "files" 23 | }: 24 | 25 | extenders: 26 | 27 | # TODO(low): Allow for imperative-style user&group definition 28 | let 29 | default-user = { 30 | # ‘user’ has no default value 31 | # ‘password-hash’ has no default value 32 | # TODO(medium): ‘uid’ has no default value for the time being 33 | # TODO(medium): ‘gid’ should actually be given by group name 34 | gecos = ""; 35 | home = "/var/empty"; 36 | shell = "/run/current-system/sw/bin/nologin"; # TODO(high): this file doesn't actually exist 37 | }; 38 | 39 | # TODO(low): this builtins.all should be a call to lib.make-attrset for better 40 | # error reporting 41 | passwd-list = 42 | assert builtins.all (e: 43 | 1 == pkgs.lib.count (x: x.user == e.user) extenders && 44 | 1 == pkgs.lib.count (x: x.uid == e.uid) extenders 45 | ) extenders; 46 | map (ext: 47 | assert ext.meta.type == "user"; 48 | let e = default-user // ext; in 49 | "${e.user}:x:${toString e.uid}:${toString e.gid}:${e.gecos}:${e.home}:${e.shell}" 50 | ) extenders; 51 | 52 | passwd-text = pkgs.lib.concatStringsSep "\n" passwd-list; 53 | 54 | shadow-list = map (e: "${e.user}:${e.password-hash}:::::::") extenders; 55 | 56 | shadow-text = pkgs.lib.concatStringsSep "\n" shadow-list; 57 | in 58 | { 59 | ${files} = [ 60 | { meta.type = "symlink"; 61 | file = "/etc/passwd"; 62 | target = pkgs.writeText "passwd" passwd-text; 63 | } 64 | 65 | # TODO(high): make /etc/shadow non-world-readable? all the data in it is 66 | # accessible from the store anyway, so… 67 | { meta.type = "symlink"; 68 | file = "/etc/shadow"; 69 | target = pkgs.writeText "shadow" shadow-text; 70 | } 71 | ]; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /nixtos/version/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | rec { 4 | nixpkgs = with pkgs.lib; 5 | if pathExists "${toString pkgs.path}/.version-suffix" then 6 | substring 0 8 ( 7 | elemAt ( 8 | splitString "." (fileContents "${toString pkgs.path}/.version-suffix") 9 | ) 1 10 | ) 11 | else if pathExists "${toString pkgs.path}/.git" then 12 | substring 0 8 (commitIdFromGitRepo "${toString pkgs.path}/.git") 13 | else "unknown-version"; 14 | 15 | nixtos = with pkgs.lib; 16 | if pathExists ./version then 17 | fileContents ./version 18 | else 19 | substring 0 8 (commitIdFromGitRepo ../../.git); 20 | 21 | name = "nixtos-${nixtos}-nixpkgs-${nixpkgs}"; 22 | } 23 | -------------------------------------------------------------------------------- /nixtos/vm-drive/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { 4 | empty-drive = import ./empty-drive { inherit pkgs top; }; 5 | guestfish = import ./guestfish { inherit pkgs top; }; 6 | virtfs = import ./virtfs { inherit pkgs top; }; 7 | virtfs-to-store = import ./virtfs-to-store { inherit pkgs top; }; 8 | } 9 | -------------------------------------------------------------------------------- /nixtos/vm-drive/empty-drive/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { size, persist, name, type ? "qcow2" }: 4 | 5 | { store }: 6 | 7 | let 8 | build-cmd = "${pkgs.kvm}/bin/qemu-img create -f ${type} ${name} ${size}"; 9 | in 10 | { 11 | build = 12 | if persist then '' 13 | if [ ! -f "${name}" ]; then 14 | ${build-cmd} 15 | fi 16 | '' else '' 17 | rm -i "${name}" 18 | ${build-cmd} 19 | ''; 20 | 21 | options = '' 22 | -drive file="${name}",if=virtio 23 | ''; 24 | } 25 | -------------------------------------------------------------------------------- /nixtos/vm-drive/guestfish/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { persist, name, script }: 4 | 5 | { store }: 6 | 7 | let 8 | script-file = pkgs.writeText "guestfish-script" script; 9 | build-cmd = "${pkgs.libguestfs}/bin/guestfish < ${script-file}"; 10 | in 11 | { 12 | build = 13 | if persist then '' 14 | if [ ! -f '${name}' ]; then 15 | ${build-cmd} 16 | fi 17 | '' else '' 18 | rm -i '${name}' 19 | ${build-cmd} 20 | ''; 21 | 22 | options = "-drive file='${name}',if=virtio"; 23 | } 24 | -------------------------------------------------------------------------------- /nixtos/vm-drive/virtfs-to-store/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { tag }: 4 | 5 | { store }: 6 | 7 | { 8 | build = ""; 9 | options = "-virtfs local,mount_tag=${tag},path=${store},security_model=none"; 10 | } 11 | -------------------------------------------------------------------------------- /nixtos/vm-drive/virtfs/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, top }: 2 | 3 | { tag, path, rw }: 4 | 5 | { store }: 6 | 7 | { 8 | build = ""; 9 | options = "-virtfs local,mount_tag=${tag},path=${path},security_model=none${ 10 | if rw then "" else ",readonly" 11 | }"; 12 | } 13 | -------------------------------------------------------------------------------- /tests/default.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import {}; 3 | nixtos = import ../nixtos { inherit pkgs; }; 4 | in 5 | with (import ../nixtos { inherit pkgs; }); 6 | 7 | let 8 | lib-tests = import ./lib { inherit pkgs nixtos; }; 9 | run-lib-tests = 10 | if lib-tests == [] then {} 11 | else throw '' 12 | Some library tests failed! 13 | 14 | ${builtins.concatStringsSep "\n" (builtins.map (f: 15 | " * ${f.name}:\n" + 16 | " Expected ${builtins.toJSON f.expected}\n" + 17 | " Got ${builtins.toJSON f.result}\n" 18 | ) lib-tests)} 19 | ''; 20 | in 21 | 22 | builtins.seq 23 | run-lib-tests 24 | (pkgs.writeScript "all-tests" '' 25 | #!${pkgs.bash}/bin/bash 26 | 27 | # TODO(medium): run VM-based tests here 28 | 29 | echo "Congratulations, all tests passed!" 30 | '') 31 | -------------------------------------------------------------------------------- /tests/example.nix: -------------------------------------------------------------------------------- 1 | # TODO(medium): Use the VM system as a way to automatically test that NixtOS 2 | # works properly. 3 | let pkgs = import {}; in 4 | with (import ../nixtos { inherit pkgs; }); 5 | 6 | let 7 | drives = [ 8 | (vm-drive.virtfs-to-store { tag = "store"; }) 9 | (vm-drive.virtfs { tag = "config"; path = ./example; rw = false; }) 10 | (vm-drive.guestfish { 11 | name = "test.img"; 12 | persist = true; 13 | script = '' 14 | disk-create test.img qcow2 2G 15 | add test.img 16 | run 17 | part-init /dev/sda mbr 18 | part-add /dev/sda p 2048 4096 19 | part-add /dev/sda p 4097 -2048 20 | mke2fs /dev/sda1 21 | mke2fs /dev/sda2 22 | ''; 23 | }) 24 | ]; 25 | 26 | os = operating-system { 27 | block-devices = { 28 | "/dev/vda" = block-device.virtio-disk {}; 29 | }; 30 | filesystems = { 31 | "/" = filesystem.tmpfs {}; 32 | "/boot" = filesystem.ext4 { block-device = "/dev/vda1"; }; 33 | "/nix/.ro-store" = filesystem.virtfs { tag = "store"; }; 34 | "/config" = filesystem.virtfs { tag = "config"; }; 35 | "/nix/store" = filesystem.overlayfs { 36 | lower = "/nix/.ro-store"; 37 | upper = "/nix/.rw-store"; 38 | work = "/nix/.work-store"; 39 | }; 40 | }; 41 | packages = with pkgs; [ 42 | (pkgs.lib.lowPrio busybox) 43 | bash coreutils curl dhcpcd gnugrep iproute kmod nix procps pstree strace 44 | ]; 45 | services = core-system { 46 | ttys = { ttyS0 = tty.agetty {}; }; 47 | } { 48 | test-env = pam.env { # TODO(high): this should be by default 49 | vars = { 50 | PATH = "/run/current-system/sw/bin"; 51 | }; 52 | }; 53 | test-users = users.unix.users [ # TODO(high): this should find a home somewhere 54 | { user = "root"; 55 | password-hash = "$5$fl7YR8nFD0jQJ$mja7t27ZM2yTTPwWeotJ2cEumZxk6a5uSiHC8i1PCN."; # "test" 56 | home = "/root"; 57 | shell = "/run/current-system/sw/bin/bash"; 58 | uid = 0; 59 | gid = 0; 60 | } 61 | { user = "nixbld1"; 62 | password-hash = "x"; 63 | uid = 1; 64 | gid = 2; 65 | } 66 | ]; 67 | test-groups = groups.unix.groups [ # TODO(high): this should find a home 68 | { group = "root"; 69 | gid = 0; 70 | users = [ "root" ]; 71 | } 72 | { group = "wheel"; 73 | gid = 1; 74 | users = [ "root" ]; 75 | } 76 | { group = "nixbld"; 77 | gid = 2; 78 | users = [ "nixbld1" ]; 79 | } 80 | ]; 81 | }; 82 | }; 83 | in 84 | { 85 | vm = build-vm { 86 | inherit drives; 87 | 88 | net = "user"; 89 | 90 | inherit os; 91 | }; 92 | 93 | graph = service-graph { 94 | inherit (os) name solved-services; 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /tests/example/configuration.nix: -------------------------------------------------------------------------------- 1 | # TODO(low): this example is outdated 2 | let pkgs = import {}; in 3 | with (import ../../nixtos { inherit pkgs; }); 4 | 5 | operating-system { 6 | block-devices = { 7 | "/dev/vda" = block-devices.virtio-disk {}; 8 | }; 9 | filesystems = { 10 | "/" = filesystem.ext4 { block-device = "/dev/vda2"; }; 11 | "/boot" = filesystem.ext4 { block-device = "/dev/vda1"; }; 12 | }; 13 | packages = with pkgs; [ bash coreutils ]; 14 | services = core-system {} { 15 | example-service = _: [ 16 | { extends = "init"; 17 | data = { 18 | type = "service"; 19 | name = "example"; 20 | script = '' 21 | #!${pkgs.bash}/bin/bash 22 | 23 | echo "This is a test service running! (but dying too early)" 24 | ''; 25 | log-script = '' 26 | #!${pkgs.bash}/bin/bash 27 | ''; 28 | }; 29 | } 30 | ]; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /tests/lib/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixtos }: 2 | 3 | let 4 | make-attrsets-tests = [ 5 | { l = [ { name = "foo"; value = 1; } { name = "bar"; value = 2; } ]; 6 | res = { foo = 1; bar = 2; }; 7 | } 8 | { l = [ { name = "foo"; value = 1; } { name = "foo"; value = 1; } ]; 9 | res = "an error"; 10 | } 11 | ]; 12 | make-attrsets-result = 13 | builtins.foldl' (acc: x: 14 | let res = nixtos.lib.make-attrset (_: "an error") x.l; in 15 | if res == x.res then acc 16 | else throw "make-attrset (…) ${builtins.toJSON x.l} = ${builtins.toJSON 17 | res} when ${builtins.toJSON x.res} was expected" 18 | ) true make-attrsets-tests; 19 | 20 | # Types used here: 21 | # test = { expr, expected } 22 | # test-result = { name, expected, result } WHERE expected != result 23 | # TODO(low): catch throws with builtins.tryEval 24 | testbed = { 25 | # map string test -> list test-result 26 | run = tests: 27 | builtins.map (name: { 28 | inherit name; 29 | expected = tests.${name}.expected; 30 | result = tests.${name}.expr; 31 | }) (builtins.filter (name: 32 | tests.${name}.expr != tests.${name}.expected 33 | ) (builtins.attrNames tests)); 34 | 35 | # map string ({ ... } -> list test-result) -> list test-result 36 | recurse = tests: 37 | builtins.concatLists ( 38 | builtins.map (name: 39 | builtins.map (test-result: { 40 | inherit (test-result) expected result; 41 | name = "${name}.${test-result.name}"; 42 | }) (tests.${name} { inherit pkgs nixtos testbed; }) 43 | ) (builtins.attrNames tests) 44 | ); 45 | }; 46 | in 47 | testbed.recurse { 48 | disjoint-union = import ./disjoint-union.nix; 49 | make-attrset = import ./make-attrset.nix; 50 | merges = import ./merges.nix; 51 | sorted-deps-of = import ./sorted-deps-of.nix; 52 | types = import ./types.nix; 53 | } 54 | -------------------------------------------------------------------------------- /tests/lib/disjoint-union.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixtos, testbed }: 2 | 3 | let 4 | disjoint-union = nixtos.lib.disjoint-union; 5 | in 6 | testbed.run { 7 | disjoint-sets = { 8 | expr = disjoint-union (_: "error") { foo = 1; } { bar = 1; }; 9 | expected = { foo = 1; bar = 1; }; 10 | }; 11 | 12 | non-disjoint-sets = { 13 | expr = disjoint-union (_: "error") { foo = 1; } { foo = 1; bar = 1; }; 14 | expected = "error"; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /tests/lib/make-attrset.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixtos, testbed }: 2 | 3 | let 4 | make-attrset = nixtos.lib.make-attrset; 5 | nv = name: value: { inherit name value; }; 6 | in 7 | testbed.run { 8 | correct-set = { 9 | expr = make-attrset (_: "error") [ (nv "foo" 1) (nv "bar" 2) ]; 10 | expected = { foo = 1; bar = 2; }; 11 | }; 12 | 13 | incorrect-set = { 14 | expr = make-attrset (_: "error") [ (nv "foo" 1) (nv "foo" 1) ]; 15 | expected = "error"; 16 | }; 17 | 18 | incorrect-set-bis = { 19 | expr = make-attrset (_: "error") [ (nv "foo" 1) (nv "foo" 2) ]; 20 | expected = "error"; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /tests/lib/merges.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixtos, testbed }: 2 | 3 | with nixtos.lib.merges; 4 | testbed.run { 5 | const-hello = { 6 | expr = const "hello" []; 7 | expected = { 8 | result = "hello"; 9 | errors = []; 10 | }; 11 | }; 12 | const-does-ignore = { 13 | expr = const "hello" [ "world" ]; 14 | expected = { 15 | result = "hello"; 16 | errors = []; 17 | }; 18 | }; 19 | 20 | attrs-disjoint-union-pass = { 21 | expr = attrs.disjoint-union [ 22 | { foo = 1; bar = 2; } 23 | { baz = 3; quux = "hello"; } 24 | ]; 25 | expected = { 26 | result = { foo = 1; bar = 2; baz = 3; quux = "hello"; }; 27 | errors = []; 28 | }; 29 | }; 30 | attrs-disjoint-union-fail = { 31 | expr = attrs.disjoint-union [ 32 | { foo = 1; bar = 2; baz = 3; } 33 | { foo = 1; bar = 5; quux = "hello"; } 34 | ]; 35 | expected = { 36 | errors = [ { 37 | path = []; 38 | error = 39 | "keys passed multiple times to disjoint union: [ \"bar\" \"foo\" ]"; 40 | } ]; 41 | }; 42 | }; 43 | 44 | product-of-const = { 45 | expr = product { a = const "hello"; b = const "world"; } []; 46 | expected = { 47 | result = { a = "hello"; b = "world"; }; 48 | errors = []; 49 | }; 50 | }; 51 | product-const-disjoint-union = { 52 | expr = product { a = const "hello"; b = attrs.disjoint-union; } [ 53 | { a = "world"; b = { foo = 1; }; } 54 | { a = 42; b = { bar = 0.1337; }; } 55 | ]; 56 | expected = { 57 | result = { a = "hello"; b = { foo = 1; bar = 0.1337; }; }; 58 | errors = []; 59 | }; 60 | }; 61 | product-const-disjoint-union-fail = { 62 | expr = product { a = const "hello"; b = attrs.disjoint-union; } [ 63 | { a = "world"; b = { foo = 1; }; } 64 | { a = 42; b = { foo = 0.1337; }; } 65 | ]; 66 | expected = { 67 | errors = [ { 68 | path = [ "b" ]; 69 | error = "keys passed multiple times to disjoint union: [ \"foo\" ]"; 70 | } ]; 71 | }; 72 | }; 73 | product-double-fail = { 74 | expr = product { a = attrs.disjoint-union; b = attrs.disjoint-union; } [ 75 | { a = { foo = 0.1337; }; b = { foo = 1; }; } 76 | { a = { foo = 1; }; b = { foo = 0.1337; }; } 77 | ]; 78 | expected = { 79 | errors = [ { 80 | path = [ "a" ]; 81 | error = "keys passed multiple times to disjoint union: [ \"foo\" ]"; 82 | } { 83 | path = [ "b" ]; 84 | error = "keys passed multiple times to disjoint union: [ \"foo\" ]"; 85 | } ]; 86 | }; 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /tests/lib/sorted-deps-of.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixtos, testbed }: 2 | 3 | let 4 | deps = a: b: builtins.elem b.id a.deps; 5 | graph = [ 6 | { id = 0; deps = []; } 7 | { id = 1; deps = [0]; } 8 | { id = 2; deps = []; } 9 | { id = 3; deps = [2 1]; } 10 | { id = 4; deps = [6]; } 11 | { id = 5; deps = [2 3]; } 12 | { id = 6; deps = [2 3 5]; } 13 | { id = 7; deps = [2]; } 14 | { id = 8; deps = [9]; } 15 | { id = 9; deps = [10]; } 16 | { id = 10; deps = [11]; } 17 | { id = 11; deps = [13]; } 18 | { id = 12; deps = [14]; } 19 | { id = 13; deps = [12]; } 20 | { id = 14; deps = [15]; } 21 | { id = 15; deps = []; } 22 | ]; 23 | tests = [ 24 | { begin = [0]; result = [0]; } 25 | { begin = [1]; result = [0 1]; } 26 | { begin = [2]; result = [2]; } 27 | { begin = [1 2]; result = [0 1 2]; } 28 | { begin = [10]; result = [15 14 12 13 11 10]; } 29 | { begin = [6 3]; result = [2 0 1 3 5 6]; } 30 | ]; 31 | in 32 | testbed.run ( 33 | builtins.listToAttrs ( 34 | pkgs.lib.imap1 (i: t: { 35 | name = "test${toString i}"; 36 | value = { 37 | expr = 38 | let 39 | start-nodes = map (builtins.elemAt graph) t.begin; 40 | end-nodes = nixtos.lib.sorted-deps-of deps graph start-nodes; 41 | in 42 | map (n: n.id) end-nodes; 43 | expected = t.result; 44 | }; 45 | }) tests 46 | ) 47 | ) 48 | -------------------------------------------------------------------------------- /tests/lib/types.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixtos, testbed }: 2 | 3 | with nixtos.lib.types; 4 | 5 | let 6 | lib = pkgs.lib; 7 | 8 | # Generate a type checker error. 9 | # expectedType is the type expected at that position 10 | # val is the value that was badly typed 11 | err = expectedType: val: { 12 | inherit val; 13 | should = expectedType.description; 14 | }; 15 | # a successful type check 16 | ok = {}; 17 | 18 | # Test the check-type function results. 19 | # type is the type to check for 20 | # val is the value that should be of type type 21 | # result is the expected check-type result 22 | test = type: val: result: { 23 | expr = check-type type val; 24 | expected = result; 25 | }; 26 | 27 | testDef = type: result: { 28 | expr = defaults type; 29 | expected = result; 30 | }; 31 | 32 | # TODO test the return type of check-type to be 33 | # nested attrs (product { should = string; val = any; }) 34 | 35 | in testbed.run ({ 36 | 37 | 38 | # -- Scalars -- 39 | 40 | testVoid = test void 23 (err void 23); 41 | 42 | testAnyInt = test any 42 ok; 43 | testAnyString = test any "foo" ok; 44 | testAnyList = test any [ 3 "45" { dont = "do this"; } "ever" ] ok; 45 | 46 | testUnitOk = test unit {} ok; 47 | testUnitFoo = test unit "foo" (err unit "foo"); 48 | 49 | testConstOk = test (const "foo") "foo" ok; 50 | testConstRecOk = test (const { a = "bar"; }) { a = "bar"; } ok; 51 | testConstNok = test (const "foo") 42 (err (const "foo") 42); 52 | testConstRecNok = let t = const { a = "bar"; }; v = { a = "foo"; }; in 53 | test t v (err t v); 54 | 55 | testBoolOk = test bool true ok; 56 | testBoolFoo = test bool 23 (err bool 23); 57 | 58 | testStringOk = test string "foo" ok; 59 | testStringFoo = test string false (err string false); 60 | 61 | testPathOk = test path /. ok; 62 | testPathFoo = test path "/nope" (err path "/nope"); 63 | 64 | testIntOk = test int 42 ok; 65 | testIntFoo = test int {} (err int {}); 66 | 67 | testFloatOk = test float 3.14 ok; 68 | testFloatOkStrange = test float 23. ok; 69 | testFloatNotInt = test float 23 (err float 23); 70 | testFloatFoo = test float [ "nope" ] (err float [ "nope" ]); 71 | 72 | 73 | # -- Recursives -- 74 | 75 | testListEmpty = test (list void) [] ok; 76 | testListIntOk = test (list int) [ 1 2 3 ] ok; 77 | testListPosFoo = test (list int) [ 1 "ahh" 3 true ] { 78 | "1" = (err int "ahh"); 79 | "3" = (err int true); 80 | }; 81 | testListOfListUnitOk = test (list (list unit)) [ [] [{}] [{} {} {}] ] ok; 82 | testListOfListUnitFoo = test (list (list unit)) [ {} [ {} "ups" ] [[]] ] { 83 | "0" = err (list unit) {}; 84 | "1"."1" = err unit "ups"; 85 | "2"."0" = err unit []; 86 | }; 87 | 88 | testAttrsEmpty = test (attrs void) {} ok; 89 | testAttrsIntOk = test (attrs int) { foo = 1; bar = 2; } ok; 90 | testAttrsIntListFoo = test (attrs int) [] (err (attrs int) []); 91 | testAttrsIntFoo = test (attrs int) { foo.bar = 1; baz = 2; quux = true; } { 92 | foo = err int { bar = 1; }; 93 | quux = err int true; 94 | }; 95 | testAttrsOfAttrsOk = test (attrs (attrs unit)) { foo.bar = {}; baz.quux = {}; } ok; 96 | testAttrsOfAttrsEmptyOk = test (attrs (attrs unit)) {} ok; 97 | testAttrsOfAttrsFoo = test (attrs (attrs unit)) { a = []; b.c.d.e = false; } { 98 | a = err (attrs unit) []; 99 | b.c = err unit { d.e = false; }; 100 | }; 101 | 102 | testListOfAttrsOk1 = test (list (attrs unit)) [] ok; 103 | testListOfAttrsOk2 = test (list (attrs unit)) [ { a = {}; } { b = {}; } ] ok; 104 | testListOfAttrsFoo = test (list (attrs unit)) 105 | [ 42 { a = {}; b.c.d = "x"; } { x = []; } {} ] 106 | { 107 | "0" = err (attrs unit) 42; 108 | "1".b = err unit { c.d = "x"; }; 109 | "2".x = err unit []; 110 | }; 111 | 112 | 113 | # -- Products -- 114 | 115 | testProductOk = test (product { name = string; age = int; }) 116 | { name = "hans"; age = 42; } ok; 117 | testProductWrongTypes = test (product { name = string; age = int; }) 118 | { name = true; age = 23.5; } 119 | { 120 | name = err string true; 121 | age = err int 23.5; 122 | }; 123 | testProductWrongField = test (product { foo = bool; }) 124 | { bar = "foo"; } 125 | (err (product { foo = bool; }) { bar = "foo"; }); 126 | testProductTooManyFields = test (product { a = int; b = int; }) 127 | { a = 1; b = 2; c = "hello"; } 128 | (err (product { a = int; b = int; }) { a = 1; b = 2; c = "hello"; }); 129 | testProductEmptyOk = test (product {}) {} ok; 130 | testProductEmptyFoo = test (product {}) { name = "hans"; } 131 | (err (product {}) { name = "hans"; }); 132 | 133 | testProductOptOk = test 134 | (product-opt { req = { a = unit; }; opt = { b = unit; }; }) 135 | { a = {}; b = {}; } ok; 136 | testProductOptNoOptOk = test 137 | (product-opt { req = { a = unit; }; opt = { b = unit; }; }) 138 | { a = {}; } ok; 139 | testProductOnlyOptNoReq = test 140 | (product-opt { req = { a = unit; }; opt = { b = unit; }; }) 141 | { b = {}; } 142 | (err (product-opt { req = { a = unit; }; opt = { b = unit; }; }) { b = {}; }) ; 143 | testProductOnlyOptOk = test 144 | (product-opt { req = {}; opt = { x = unit; y = unit; }; }) 145 | { y = {}; } ok; 146 | testProductInProductOpt = test 147 | (product-opt { req = {}; opt = { p = product { x = int; y = bool; }; }; }) 148 | { p = { x = 23; }; } # missing the required p.y 149 | { p = (err (product { x = int; y = bool; }) { x = 23; }); }; 150 | 151 | testProductOptOpenOk = test 152 | (product-opt { req = { a = unit; }; opt = {}; open = true; }) 153 | { a = {}; rest1 = 12; rest2 = "foo"; } ok; 154 | testProductOptOpenIgnoreRest = test 155 | (product-opt { req = { a = unit; }; opt = { b = int; }; open = true; }) 156 | { a = 23; b = false; rest = "foo"; x = {}; y = 23; } 157 | { a = err unit 23; b = err int false; }; 158 | 159 | 160 | # -- Sums -- 161 | 162 | testSumLeftOk = test (sum { left = string; right = unit; }) 163 | { left = "errör!"; } ok; 164 | testSumRightOk = test (sum { left = string; right = unit; }) 165 | { right = {}; } ok; 166 | testSumWrongField = test (sum { a = int; b = bool; }) 167 | { c = "ups"; } 168 | (err (sum { a = int; b = bool; }) { c = "ups"; }); 169 | testSumIsNotUnion = test (sum { a = string; b = int; }) 170 | 42 171 | (err (sum { a = string; b = int; }) 42); 172 | testSumTooManyFields = test (sum { a = int; b = unit; }) 173 | { a = 21; b = {}; } 174 | (err (sum { a = int; b = unit; }) { a = 21; b = {}; }); 175 | 176 | 177 | # -- Unions -- 178 | 179 | testUnionOk1 = test (union [ int string (list unit) ]) 23 ok; 180 | testUnionOk2 = test (union [ int string (list unit) ]) "foo" ok; 181 | testUnionOk3 = test (union [ int string (list unit) ]) [{}{}] ok; 182 | testUnionWrongType = test (union [ int string ]) true 183 | (err (union [ int string ]) true); 184 | testUnionOne = test (union [ int ]) 23 ok; 185 | testUnionSimilar = test (union [ (list string) (attrs string) ]) 186 | { foo = "string"; } ok; 187 | 188 | } // 189 | 190 | 191 | # -- Restrictions -- 192 | 193 | (let 194 | even = restrict { 195 | type = int; 196 | check = v: lib.mod v 2 == 0; 197 | description = "even integer"; 198 | }; 199 | 200 | intBetween = min: max: restrict { 201 | type = int; 202 | check = v: v >= min && v <= max; 203 | description = "int between ${toString min} ${toString max}"; 204 | }; 205 | 206 | thirdElementIsListOf23 = restrict { 207 | type = list (list int); 208 | check = v: builtins.length v >= 3 && builtins.elemAt v 2 == [23]; 209 | description = "third element is [23]"; 210 | }; 211 | 212 | enum = t: xs: restrict { 213 | type = t; 214 | check = v: lib.any (x: x == v) xs; 215 | description = "one of values [ " + 216 | lib.concatMapStringsSep ", " (lib.generators.toPretty {}) xs 217 | + " ]"; 218 | }; 219 | 220 | transitive = restrict { 221 | description = "neither a nor b"; 222 | check = v: v != "b"; 223 | type = restrict { 224 | description = "not a"; 225 | check = v: v != "a"; 226 | type = string; 227 | }; 228 | }; 229 | 230 | in { 231 | testRestrictEvenOk = test (list even) 232 | [ 2 4 128 42 ] ok; 233 | testRestrictEvenFoo = test (list even) 234 | [ 42 23 ] 235 | { "1" = err even 23; }; 236 | 237 | testRestrictTypeCheckFirst = 238 | let t = restrict { 239 | type = void; 240 | # will crash if "23" is checked here 241 | # before the check for its type 242 | check = v: (v + 19) == 42; 243 | description = "is worthy"; 244 | }; 245 | # we actually want it to always give the type description 246 | # of the restricted type when the general type check fails 247 | # e.g. 23 should return "even integer" instead of "integer" 248 | # when checking for the int restricted to even integers 249 | in test t "23" (err t "23"); 250 | 251 | testDeepRestriction = test thirdElementIsListOf23 252 | [ [1] ["foo"] [23] ] 253 | { "1"."0" = err int "foo"; }; 254 | testDeepRestrictionFoo = test thirdElementIsListOf23 255 | [ [] [] [24] [] [] ] 256 | (err thirdElementIsListOf23 [ [] [] [24] [] [] ]); 257 | 258 | testRestrictTransitiveOk = test transitive "c" ok; 259 | testRestrictTransitiveA = test transitive "a" 260 | (err transitive "a"); 261 | testRestrictTransitiveB = test transitive "b" 262 | (err transitive "b"); 263 | 264 | testRestrictIntBetweenOk = test (list (intBetween (-2) 3)) 265 | [ (-2) (-1) 0 1 2 3 ] ok; 266 | testRestrictIntBetweenFoo = test (list (intBetween 0 0)) 267 | [ (-23) 42 ] 268 | { "0" = err (intBetween 0 0) (-23); 269 | "1" = err (intBetween 0 0) 42; }; 270 | 271 | testRestrictEnumOk = test (list (enum string [ "a" "b" "c" ])) 272 | [ "b" "c" "a" ] ok; 273 | testRestrictEnumFoo = test (list (enum string [ "a" "b" "c" ])) 274 | [ "b" "d" "a" ] 275 | { "1" = err (enum string [ "a" "b" "c" ]) "d"; }; 276 | 277 | })) 278 | --------------------------------------------------------------------------------