liminix/pkgs/systemconfig/default.nix

109 lines
3.6 KiB
Nix

# 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
'';
}