# The ideal is that a Liminix system can boot with only the files in # /nix/store. This package generates a small program that is run at early # boot (from the initramfs) to populate directories such as /etc, # /bin, /home according to whatever the configuration says # they should contain { writeText , writeFennelScript , lib , s6-init-bin , closureInfo , stdenv }: let inherit (lib.attrsets) mapAttrsToList; escaped = msg : builtins.replaceStrings ["\n" "=" "\"" "$" ] ["\\x0a" "\\x3d" "\\x22" "\\x24"] msg; visit = prefix: attrset: let makeFile = prefix : filename: { type ? "f" , mode ? null , target ? null , contents ? null , file ? null , major ? null , minor ? null , uid ? 0 , gid ? 0 }: let pathname = "${prefix}/${filename}"; qpathname = builtins.toJSON pathname; mode' = if mode != null then mode else (if type == "d" then "0755" else "0644"); cmds = { "f" = "PRINTFILE(${qpathname}, ${mode'}, ${builtins.toJSON (escaped file)});"; "d" = "MKDIR(${qpathname}, ${mode'});\n" + (builtins.concatStringsSep "\n" (visit pathname contents)); "c" = "MKNOD_C(${qpathname}, ${mode'}, ${major}, ${minor});"; "b" = "MKNOD_B(${qpathname}, ${mode'}, ${major}, ${minor});"; "s" = "LN_S(${builtins.toJSON target}, ${qpathname});"; "l" = "LN(${builtins.toJSON target}, ${qpathname})"; "i" = "MKNOD_P(${qpathname}, ${mode'});"; }; cmd = cmds.${type}; chown = if uid>0 || gid>0 then "\nCHOWN(${qpathname},${toString uid},${toString gid});\n" else ""; in "unlink(${qpathname}); ${cmd} ${chown}"; in mapAttrsToList (makeFile prefix) attrset; activateScript = attrset: writeText "makedevs.c" '' #include "defs.h" int main(int argc, char* argv[]) { chdir(argv[1]); ${(builtins.concatStringsSep "\n" (visit "." attrset))} } ''; in attrset: let makedevs = activateScript attrset; in stdenv.mkDerivation { name="make-stuff"; src = ./.; CFLAGS = "-Os"; LDFLAGS = "-static -Xlinker -static"; postConfigure = '' cp ${makedevs} makedevs.c ''; makeFlags = ["makedevs"]; installPhase = '' closure=${closureInfo { rootPaths = [ makedevs ]; }} mkdir -p $out/bin $out/etc cp $closure/store-paths $out/etc/nix-store-paths $STRIP --remove-section=.note --remove-section=.comment --strip-all makedevs -o $out/bin/activate ln -s ${s6-init-bin}/bin/init $out/bin/init cp -p ${writeFennelScript "restart-services" [] ./restart-services.fnl} $out/bin/restart-services cat > $out/bin/install <<EOF #!/bin/sh -e prefix=\''${1-/} src=\''${prefix}$out dest=\$prefix ${# if we are running on a normal mounted system then # the actual device root is mounted on /persist # and /nix is bind mounted from /persist/nix # (see the code in preinit). So we need to check for this # case otherwise we will install into a ramfs/rootfs "" } if test -d \$dest/persist; then dest=\$dest/persist; fi cp -v -fP \$src/bin/* \$src/etc/* \$dest ${if attrset ? boot then '' (cd \$dest if test -e boot ; then rm boot ; fi ln -sf ${lib.strings.removePrefix "/" attrset.boot.target} ./boot ) '' else ""} EOF chmod +x $out/bin/install ''; }