Compare commits

...

14 Commits

Author SHA1 Message Date
Daniel Barlow af4cd2e05c make mksquashfs fail on error 2022-09-27 17:36:25 +01:00
Daniel Barlow a5e3fffa12 what's so amazing about really deep thoughts? 2022-09-27 16:48:17 +01:00
Daniel Barlow 5d10a9e760 rename config.environment as config.filesystem 2022-09-27 16:48:17 +01:00
Daniel Barlow 696bbe6521 rename systemPackages as defaultProfile.packages
it doesn't work the same way as in nixos, so don't name it the same way
2022-09-27 16:47:42 +01:00
Daniel Barlow 4cbe669783 move make-image to subdirectory 2022-09-27 16:47:42 +01:00
Daniel Barlow b7ff973b4b explain package/module distinction, add notes on side tracks 2022-09-27 14:11:23 +01:00
Daniel Barlow e8880c199b allow overriding init 2022-09-27 14:06:39 +01:00
Daniel Barlow e40a91fca0 move all remaining pseudofiles to base module 2022-09-27 14:06:07 +01:00
Daniel Barlow 797aa30c47 make s6-init-files into a module 2022-09-27 10:19:44 +01:00
Daniel Barlow 85f7f7293d add bin/init to s6-rc module
... which suggests it could be better named; init is from
s6-linux-init not s6-rc
2022-09-26 21:11:07 +01:00
Daniel Barlow 4c89e9aee6 add passwd and group in environment.etc 2022-09-26 21:02:10 +01:00
Daniel Barlow a427b9da5e move s6-rc db creation to a module 2022-09-26 20:59:56 +01:00
Daniel Barlow bd6cbd373f add config environment.etc which gets converted to pseudofiles 2022-09-26 20:46:28 +01:00
Daniel Barlow 09a9dba963 export "dir" and "symlink" from pseudofiles package 2022-09-26 18:27:43 +01:00
19 changed files with 346 additions and 237 deletions

View File

@ -23,3 +23,10 @@ expression or there is more than one reference to `up`, `down` etc.
* where a `let` form defines multiple names, put a newline after the * where a `let` form defines multiple names, put a newline after the
token `let`, and indent each name two characters token `let`, and indent each name two characters
* should it be a package or a module? packages are self-contained -
they live in /nix/store/eeeeeee-name and don't directly change
system behaviour by their presence or absense. modules can add to
/etc or /bin or other global state, create services, all that
side-effecty stuff. generally it should be a package unless it
can't be.

View File

@ -129,3 +129,85 @@ DONE 17) rename nixwrt references in kernel builder
19) syslogd - use busybox or s6? 19) syslogd - use busybox or s6?
chat -s -S ogin:--ogin: root / "ip address show dev ppp0 | grep ppp0" 192.168.100.1 "/nix/store/*-s6-linux-init-*/bin/s6-linux-init-hpr -p" chat -s -S ogin:--ogin: root / "ip address show dev ppp0 | grep ppp0" 192.168.100.1 "/nix/store/*-s6-linux-init-*/bin/s6-linux-init-hpr -p"
Working towards a general goal of having a derivation we can
usefully run `nix path-info` on - or some other tool that will
tell us what's making the images big. The squashfs doesn't
have this information.
Towards that end (really? can't remember how ...) what would be a
way for packages to declare "I want to add files to /etc"? Is that
even a good idea?
Thinking we should turn s6-init-files back into a real derivation.
Tue Sep 27 00:31:45 BST 2022
> Thinking we should turn s6-init-files back into a real derivation.
This turns out to be Not That Simple, because it contains weird shit
(sticky bits and fifos).
Tue Sep 27 09:50:44 BST 2022
* allow modules to register activation scripts that are run on the
root filesystem once all packages are installed
- do they run on build or on host? if we're upgrading in place
how do we ship filesystem changes to the host?
or:
* allow modules to declare environment.*, use pseudofile on build and
create real files on host. will need to keep the implementation on
host faily simple because restricted environment
Tue Sep 27 16:14:18 BST 2022
TODO list is getting both longer and shorter, though longer on
average.
DONE 1) rename config.environment as .filesystem. and add module
types for it
2) perhaps we shouldn't use process-based services like [ou]dhcp as
queryable endpoint for interface addresses (e.g. when adding routes).
Instead have a separate service for interface address that depends on
the *dhcp and uses its output
3) when I killed ppp it restarted, but I don't think it reran
defaultroute which is supposed to depend on it. (Might be important
e.g. if we'd been assigned a different IP address). Investigate
semantics of s6-rc service dependencies
4) figure out a nice way to fit ppp into this model as it actually
creates the interface instead of using an existing unconfigured one
5) write a test for udhcp
6) squashfs size is ~ 14MB for a configuration with not much in it,
look for obvious wastes of space
7) some of the pppoe config should be moved into a ppp service
8) some of configuration.nix (e.g. defining routes) should be moved into
tools
11) haven't done (overlayfs) overlays at all
13) upgrade ppp to something with an ipv6-up-script option, move ppp and pppoe derivations into their own files
14) add ipv6 support generally
15) "ip address add" seems to magically recognise v4 vs v6 but
is that specified or fluke?
18) maybe stop suffixing all the service names with .service
19) syslogd - use busybox or s6?
20) The option currently called defaultPackages needs a better name as
it doesn't have the same semantics as nixos
environment.defaultPackages. maybe call it packagesInProfile or
packagesOnPath. or defaultProfile.packages

View File

@ -4,19 +4,16 @@
let let
overlay = import ./overlay.nix; overlay = import ./overlay.nix;
nixpkgs = import <nixpkgs> ( device.system // {overlays = [overlay]; }); nixpkgs = import <nixpkgs> ( device.system // {overlays = [overlay]; });
inherit (nixpkgs.pkgs) callPackage liminix;
config = (import ./merge-modules.nix) [ config = (import ./merge-modules.nix) [
./modules/base.nix ./modules/base.nix
({ lib, ... } : { config = { inherit (device) kernel; }; }) ({ lib, ... } : { config = { inherit (device) kernel; }; })
<liminix-config> <liminix-config>
./modules/s6
] nixpkgs.pkgs; ] nixpkgs.pkgs;
finalConfig = config // { squashfs = liminix.builders.squashfs config;
packages = (with nixpkgs.pkgs; [ s6-init-files s6-rc ]) ++ kernel = callPackage ./kernel {
config.systemPackages ++ inherit (config.kernel) config;
(builtins.attrValues config.services);
};
squashfs = (nixpkgs.pkgs.callPackage ./make-image.nix {}) finalConfig;
kernel = nixpkgs.pkgs.callPackage ./kernel {
inherit (finalConfig.kernel) config;
}; };
in { in {
outputs = { outputs = {

View File

@ -1,67 +0,0 @@
{
stdenv
, busybox
, buildPackages
, callPackage
, execline
, lib
, runCommand
, s6-init-bin
, s6-init-files
, s6-linux-init
, s6-rc
, s6-rc-database
, stdenvNoCC
, writeScript
, writeText
} : config :
let
s6-rc-db = s6-rc-database.override {
services = builtins.attrValues config.services;
};
profile = writeScript ".profile" ''
PATH=${lib.makeBinPath ([ s6-init-bin busybox execline s6-linux-init s6-rc])}
export PATH
'';
pseudofiles = writeText "pseudofiles" ''
/ d 0755 0 0
/bin d 0755 0 0
/etc d 0755 0 0
/run d 0755 0 0
/dev d 0755 0 0
/dev/null c 0666 root root 1 3
/dev/zero c 0666 root root 1 5
/dev/tty1 c 0777 root root 4 1
/dev/tty2 c 0777 root root 4 2
/dev/tty3 c 0777 root root 4 3
/dev/tty4 c 0777 root root 4 4
/dev/tty c 0777 root root 5 0
/dev/console c 0600 root root 5 1
/proc d 0555 root root
/sys d 0555 root root
/dev/pts d 0755 0 0
/etc/init.d d 0755 0 0
/bin/init s 0755 0 0 ${s6-init-bin}/bin/init
/bin/sh s 0755 0 0 ${busybox}/bin/sh
/bin/busybox s 0755 0 0 ${busybox}/bin/busybox
/etc/s6-rc d 0755 0 0
/etc/s6-rc/compiled s 0755 0 0 ${s6-rc-db}/compiled
/etc/passwd f 0644 0 0 echo "root::0:0:root:/:/bin/sh"
/.profile s 0644 0 0 ${profile}
'';
storefs = callPackage <nixpkgs/nixos/lib/make-squashfs.nix> {
# add pseudofiles to store so that the packages they
# depend on are also added
storeContents = [ pseudofiles s6-init-files ] ++ config.packages ;
};
in runCommand "frob-squashfs" {
nativeBuildInputs = with buildPackages; [ squashfsTools qprint ];
} ''
cp ${storefs} ./store.img
chmod +w store.img
mksquashfs - store.img -no-recovery -quiet -no-progress -root-becomes store -p "/ d 0755 0 0"
mksquashfs - store.img -no-recovery -quiet -no-progress -root-becomes nix -pf ${pseudofiles} -pf ${s6-init-files}
cp store.img $out
''

View File

@ -1,6 +1,9 @@
{ lib, ...}: { lib, pkgs, config, ...}:
let let
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ; inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) busybox;
type_service = types.package // { type_service = types.package // {
name = "service"; name = "service";
description = "s6-rc service"; description = "s6-rc service";
@ -9,12 +12,17 @@ let
in { in {
options = { options = {
systemPackages = mkOption { # analogous to nixos systemPackages, but we don't symlink into
# /run/current-system, we just add the paths in /etc/profile
defaultProfile = {
packages = mkOption {
type = types.listOf types.package; type = types.listOf types.package;
}; };
};
services = mkOption { services = mkOption {
type = types.attrsOf type_service; type = types.attrsOf type_service;
}; };
filesystem = mkOption { type = types.anything; };
kernel = { kernel = {
config = mkOption { config = mkOption {
# mostly the values are y n or m, but sometimes # mostly the values are y n or m, but sometimes
@ -26,4 +34,35 @@ in {
}; };
}; };
}; };
config = {
defaultProfile.packages = with pkgs;
[ s6-init-bin busybox execline s6-linux-init s6-rc ];
filesystem = dir {
bin = dir {
sh = symlink "${busybox}/bin/sh";
busybox = symlink "${busybox}/bin/busybox";
};
dev =
let node = type: major: minor: mode : { inherit type major minor mode; };
in dir {
null = node "c" "1" "3" "0666";
zero = node "c" "1" "5" "0666";
tty = node "c" "5" "0" "0666";
console = node "c" "5" "1" "0600";
pts = dir {};
};
etc = dir {
profile = symlink
(pkgs.writeScript ".profile" ''
PATH=${lib.makeBinPath config.defaultProfile.packages}
export PATH
'');
passwd = { file = "root::0:0:root:/:/bin/sh\n"; };
group = { file = "root::0:\n"; };
};
proc = dir {};
run = dir {};
sys = dir {};
};
};
} }

158
modules/s6/default.nix Normal file
View File

@ -0,0 +1,158 @@
{ config, pkgs, ... }:
let
inherit (pkgs)
busybox
execline
s6
s6-init-bin
s6-linux-init
stdenvNoCC;
inherit (pkgs.pseudofile) dir symlink;
s6-rc-db = pkgs.s6-rc-database.override {
services = builtins.attrValues config.services;
};
s6-init-scripts = stdenvNoCC.mkDerivation {
name = "s6-scripts";
src = ./scripts;
phases = ["unpackPhase" "installPhase" ];
buildInputs = [busybox];
installPhase = ''
mkdir $out
cp -r $src $out/scripts
chmod -R +w $out
patchShebangs $out/scripts
'';
};
service = dir {
s6-linux-init-runleveld = dir {
notification-fd = { file = "3"; };
run = {
file = ''
#!${execline}/bin/execlineb -P
${execline}/bin/fdmove -c 2 1
${execline}/bin/fdmove 1 3
${s6}/bin/s6-ipcserver -1 -a 0700 -c 1 -- s
${s6}/bin/s6-sudod -dt30000 -- "/etc/s6-linux-init/current"/scripts/runlevel
'';
mode = "0755";
};
};
s6-linux-init-shutdownd = dir {
fifo = {
type = "i";
subtype = "f";
mode = "0600";
};
run = {
file = ''
#!${execline}/bin/execlineb -P
${s6-linux-init}/bin/s6-linux-init-shutdownd -c "/etc/s6-linux-init/current" -g 3000
'';
mode = "0755";
};
};
s6-svscan-log = dir {
fifo = {
type = "i";
subtype = "f";
mode = "0600";
};
notification-fd = { file = "3"; };
run = {
file = ''
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 1 /dev/null
${execline}/bin/redirfd -rnb 0 fifo
${s6}/bin/s6-log -bpd3 -- t /run/uncaught-logs
'';
mode = "0755";
};
};
getty = dir {
run = {
file = ''
#!${execline}/bin/execlineb -P
${busybox}/bin/getty -l ${busybox}/bin/login 38400 /dev/console
'';
mode = "0755";
};
};
".s6-svscan" =
let
quit = message: ''
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 2 /dev/console
${execline}/bin/fdmove -c 1 2
${execline}/bin/foreground { ${s6-linux-init}/bin/s6-linux-init-echo -- ${message} }
${s6-linux-init}/bin/s6-linux-init-hpr -fr
'';
shutdown = action: ''
#!${execline}/bin/execlineb -P
${s6-linux-init}/bin/s6-linux-init-hpr -a #{action} -- now
'';
empty = "#!${execline}/bin/execlineb -P\n";
in dir {
crash = {
file = quit "s6-svscan crashed. Rebooting.";
mode = "0755";
};
finish = {
file = quit "s6-svscan exited. Rebooting.";
mode = "0755";
};
SIGINT = {
file = shutdown "-r";
mode = "0755";
};
SIGPWR = {
file = shutdown "-p";
mode = "0755";
};
SIGQUIT = {
file = empty;
mode = "0755";
};
SIGTERM = {
file = empty;
mode = "0755";
};
SIGUSR1 = {
file = shutdown "-p";
mode = "0755";
};
SIGUSR2 = {
file = shutdown "-h";
mode = "0755";
};
SIGWINCH = {
file = empty;
mode = "0755";
};
};
};
in {
config = {
filesystem = dir {
etc = dir {
s6-rc = dir {
compiled = symlink "${s6-rc-db}/compiled";
};
s6-linux-init = dir {
current = dir {
scripts = symlink "${s6-init-scripts}/scripts";
env = dir {};
run-image = dir {
uncaught-logs = (dir {}) // {mode = "2750";};
inherit service;
};
};
};
};
bin = dir {
init = symlink "${s6-init-bin}/bin/init";
};
};
};
}

View File

@ -1,15 +1,15 @@
final: prev: { final: prev: {
pseudofile = final.callPackage ./pkgs/pseudofile {}; pseudofile = final.callPackage ./pkgs/pseudofile {};
s6-init-files = final.callPackage ./pkgs/s6-init-files {};
strace = prev.strace.override { libunwind = null; }; strace = prev.strace.override { libunwind = null; };
liminix = { liminix = {
services = final.callPackage ./pkgs/liminix-tools/services {}; services = final.callPackage ./pkgs/liminix-tools/services {};
networking = final.callPackage ./pkgs/liminix-tools/networking {}; networking = final.callPackage ./pkgs/liminix-tools/networking {};
builders = {
squashfs = final.callPackage ./pkgs/liminix-tools/builders/squashfs.nix {};
};
}; };
writeAshScript = final.callPackage ./pkgs/write-ash-script {}; writeAshScript = final.callPackage ./pkgs/write-ash-script {};
s6-init-bin = final.callPackage ./pkgs/s6-init-bin {}; s6-init-bin = final.callPackage ./pkgs/s6-init-bin {};
s6-rc-database = final.callPackage ./pkgs/s6-rc-database {}; s6-rc-database = final.callPackage ./pkgs/s6-rc-database {};
pppoe = prev.rpPPPoE.overrideAttrs (o: { pppoe = prev.rpPPPoE.overrideAttrs (o: {

View File

@ -0,0 +1,29 @@
{
stdenv
, busybox
, buildPackages
, callPackage
, pseudofile
, runCommand
, writeText
} : config :
let
pseudofiles =
pseudofile.write "files.pf" (config.filesystem.contents);
storefs = callPackage <nixpkgs/nixos/lib/make-squashfs.nix> {
# 1) Every required package is referenced from somewhere
# outside /nix/store. 2) Every file outside the store is
# specified by config.filesystem. 3) Therefore, closing over
# the pseudofile will give us all the needed packages
storeContents = [ pseudofiles ];
};
in runCommand "frob-squashfs" {
nativeBuildInputs = with buildPackages; [ squashfsTools qprint ];
} ''
cp ${storefs} ./store.img
chmod +w store.img
mksquashfs - store.img -exit-on-error -no-recovery -quiet -no-progress -root-becomes store -p "/ d 0755 0 0"
mksquashfs - store.img -exit-on-error -no-recovery -quiet -no-progress -root-becomes nix -p "/ d 0755 0 0" -pf ${pseudofiles}
cp store.img $out
''

View File

@ -2,9 +2,7 @@
writeText writeText
, lib , lib
}: }:
filename : attrset :
let let
inherit (lib.debug) traceSeqN;
inherit (lib.attrsets) mapAttrsToList; inherit (lib.attrsets) mapAttrsToList;
visit = prefix: attrset: visit = prefix: attrset:
let let
@ -23,6 +21,10 @@ let
else if attrs'.type == "d" then else if attrs'.type == "d" then
(visit "${prefix}/${filename}" attrs.contents) + (visit "${prefix}/${filename}" attrs.contents) +
"\n" + line "\n" + line
else if attrs'.type == "c" then
with attrs'; "${line} ${major} ${minor}"
else if attrs'.type == "b" then
with attrs'; "${line} ${major} ${minor}"
else if attrs'.type == "s" then else if attrs'.type == "s" then
"${line} ${attrs'.target}" "${line} ${attrs'.target}"
else if attrs'.type == "l" then else if attrs'.type == "l" then
@ -33,5 +35,8 @@ let
line) line)
attrset; attrset;
in builtins.concatStringsSep "\n" l; in builtins.concatStringsSep "\n" l;
res = (visit "" attrset); in {
in writeText filename res write = filename : attrset : writeText filename (visit "" attrset);
dir = contents: { type = "d"; inherit contents; };
symlink = target: { type = "s"; inherit target; };
}

View File

@ -1,144 +0,0 @@
{
execline
, s6
, s6-linux-init
, s6-rc
, pseudofile
, lib
, stdenvNoCC
, busybox
} :
let
initscripts = stdenvNoCC.mkDerivation {
name = "s6-scripts";
src = ./scripts;
phases = ["unpackPhase" "installPhase" ];
buildInputs = [busybox];
installPhase = ''
mkdir $out
cp -r $src $out/scripts
chmod -R +w $out
patchShebangs $out/scripts
'';
};
dir = contents: { type = "d"; inherit contents; };
symlink = target: { type = "s"; inherit target; };
scripts = symlink "${initscripts}/scripts";
env = dir {};
run-image = dir {
service = dir {
s6-linux-init-runleveld = dir {
notification-fd = { file = "3"; };
run = {
file = ''
#!${execline}/bin/execlineb -P
${execline}/bin/fdmove -c 2 1
${execline}/bin/fdmove 1 3
${s6}/bin/s6-ipcserver -1 -a 0700 -c 1 -- s
${s6}/bin/s6-sudod -dt30000 -- "/etc/s6-linux-init/current"/scripts/runlevel
'';
mode = "0755";
};
};
s6-linux-init-shutdownd = dir {
fifo = {
type = "i";
subtype = "f";
mode = "0600";
};
run = {
file = ''
#!${execline}/bin/execlineb -P
${s6-linux-init}/bin/s6-linux-init-shutdownd -c "/etc/s6-linux-init/current" -g 3000
'';
mode = "0755";
};
};
s6-svscan-log = dir {
fifo = {
type = "i";
subtype = "f";
mode = "0600";
};
notification-fd = { file = "3"; };
run = {
file = ''
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 1 /dev/null
${execline}/bin/redirfd -rnb 0 fifo
${s6}/bin/s6-log -bpd3 -- t /run/uncaught-logs
'';
mode = "0755";
};
};
getty = dir {
run = {
file = ''
#!${execline}/bin/execlineb -P
${busybox}/bin/getty -l ${busybox}/bin/login 38400 /dev/console
'';
mode = "0755";
};
};
".s6-svscan" =
let
quit = message: ''
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 2 /dev/console
${execline}/bin/fdmove -c 1 2
${execline}/bin/foreground { ${s6-linux-init}/bin/s6-linux-init-echo -- ${message} }
${s6-linux-init}/bin/s6-linux-init-hpr -fr
'';
shutdown = action: ''
#!${execline}/bin/execlineb -P
${s6-linux-init}/bin/s6-linux-init-hpr -a #{action} -- now
'';
empty = "#!${execline}/bin/execlineb -P\n";
in dir {
crash = {
file = quit "s6-svscan crashed. Rebooting.";
mode = "0755";
};
finish = {
file = quit "s6-svscan exited. Rebooting.";
mode = "0755";
};
SIGINT = {
file = shutdown "-r";
mode = "0755";
};
SIGPWR = {
file = shutdown "-p";
mode = "0755";
};
SIGQUIT = {
file = empty;
mode = "0755";
};
SIGTERM = {
file = empty;
mode = "0755";
};
SIGUSR1 = {
file = shutdown "-p";
mode = "0755";
};
SIGUSR2 = {
file = shutdown "-h";
mode = "0755";
};
SIGWINCH = {
file = empty;
mode = "0755";
};
};
};
uncaught-logs = (dir {}) // {mode = "2750";};
};
structure = { etc = dir { s6-linux-init = dir { current = dir {
inherit scripts env run-image;
};};};};
in pseudofile "pseudo.s6-init" structure

View File

@ -11,10 +11,12 @@ else
flags="-serial mon:stdio" flags="-serial mon:stdio"
fi fi
INIT=${INIT-/bin/init}
qemu-system-mips \ qemu-system-mips \
-M malta -m 256 \ -M malta -m 256 \
-echr 16 \ -echr 16 \
-append "default console=ttyS0,38400n8 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/vda" \ -append "default console=ttyS0,38400n8 panic=10 oops=panic init=$INIT loglevel=8 root=/dev/vda" \
-drive file=$2,format=raw,readonly=on,if=virtio \ -drive file=$2,format=raw,readonly=on,if=virtio \
-netdev socket,id=access,mcast=230.0.0.1:1234 \ -netdev socket,id=access,mcast=230.0.0.1:1234 \
-device virtio-net-pci,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02 \ -device virtio-net-pci,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02 \

View File

@ -68,5 +68,5 @@ in rec {
]; ];
}; };
systemPackages = [ pkgs.hello ] ; defaultProfile.packages = [ pkgs.hello ] ;
} }

View File

@ -4,8 +4,7 @@ expr=$(cat <<"EXPR"
let let
overlay = import <liminix/overlay.nix>; overlay = import <liminix/overlay.nix>;
nixpkgs = import <nixpkgs> { overlays = [overlay]; }; nixpkgs = import <nixpkgs> { overlays = [overlay]; };
structure = import ./structure.nix; in nixpkgs.pkgs.callPackage ./test.nix {}
in nixpkgs.pkgs.pseudofile "pseudo.s6-init" structure
EXPR EXPR
) )

View File

@ -1,5 +1,7 @@
let {
dir = contents: { type = "d"; inherit contents; }; pseudofile
}: let
inherit (pseudofile) dir;
structure = { structure = {
service = dir { service = dir {
s6-linux-init-runleveld = dir { s6-linux-init-runleveld = dir {
@ -42,4 +44,4 @@ let
}; };
uncaught-logs = (dir {}) // {mode = "2750";}; uncaught-logs = (dir {}) // {mode = "2750";};
}; };
in structure in pseudofile.write "pseudo.s6-init" structure

View File

@ -63,5 +63,5 @@ in rec {
contents = with services; [ loopback ntp defaultroute4 ]; contents = with services; [ loopback ntp defaultroute4 ];
}; };
systemPackages = [ pkgs.hello ] ; defaultProfile.packages = [ pkgs.hello ] ;
} }