commit 4b118bca19b29c9427f3c0cd9a0005695637f46e Author: Daniel Barlow Date: Mon Sep 19 23:51:38 2022 +0100 generate a mips squashfs with some s6-rc services haven't even tried booting it but I don't expect it to work. Needs - s6-linux-init or some other pid 1 program - a kernel diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..3070356 --- /dev/null +++ b/default.nix @@ -0,0 +1,21 @@ +{ device ? (import devices/gl-ar750.nix) +}: + +let + overlay = import ./overlay.nix; + nixpkgs = import ( device.system // {overlays = [overlay]; }); + config = (import ) { + config = { + systemPackages = []; + services = {}; + }; + tools = nixpkgs.pkgs.callPackage ./tools {}; + inherit (nixpkgs) pkgs; + }; + finalConfig = config // { + packages = (with nixpkgs.pkgs; [ s6-rc ]) ++ + config.systemPackages ++ + (builtins.attrValues config.services) + ; + }; +in (import ./make-image.nix) nixpkgs finalConfig diff --git a/devices/gl-ar750.nix b/devices/gl-ar750.nix new file mode 100644 index 0000000..51e27d8 --- /dev/null +++ b/devices/gl-ar750.nix @@ -0,0 +1,11 @@ +{ + system = { + crossSystem = { + config = "mips-unknown-linux-musl"; + gcc = { + abi = "32"; + arch = "mips32"; # maybe mips_24kc- + }; + }; + }; +} diff --git a/example-configuration.nix b/example-configuration.nix new file mode 100644 index 0000000..9eedc29 --- /dev/null +++ b/example-configuration.nix @@ -0,0 +1,57 @@ +{ config, tools, pkgs } : +let + inherit (tools.networking) interface address udhcpc odhcpc; + inherit (tools.services) oneshot longrun bundle output; +in rec { + services.loopback = + let iface = interface { type = "loopback"; device = "lo";}; + in bundle { + name = "loopback"; + contents = [ + (address iface { family = "inet4"; addr ="127.0.0.1";}) + (address iface { family = "inet6"; addr ="::1";}) + ]; + }; + services.dhcpv4 = + let iface = interface { type = "hardware"; device = "eth0"; }; + in udhcpc iface {}; + + services.dhcpv6 = + let iface = interface { type = "hardware"; device = "eth0"; }; + in odhcpc iface { uid = "e7"; }; + + services.ntp = longrun { + # the simplest approach at the consumer end is to require the + # producer to create a file per output variable. + name = "ntp"; + run = let s = services; + r = "${pkgs.ntp}/bin/ntp $(cat ${output s.dhcpv4 "ntp_servers"}) $(cat ${output s.dhcpv6 "NTP_IP"})"; + in (builtins.trace r r); + # I don't think it's possible to standardise the file names + # generally, as different services have different outputs, but it + # would be cool if services that provide an interface could use + # the same name as each other. e.g. for anything implementing + # addressProvider you might expect (output svc "address") or + # (output svc "family") to work. Otherwise switching a network link + # from static to dhcp might require reviewing all the downstreams + # that refer to it. + # Also, services should declare the outputs they provide + outputs = []; + dependencies = [services.dhcpv4]; + }; + + services.defaultroute4 = + let s = services; + in oneshot { + name = "defaultroute4"; + up = '' + ip route add default gw $(cat ${output s.dhcpv4 "address"}) + echo "1" > /sys/net/ipv4/$(cat ${output s.dhcpv4 "ifname"}) + ''; + down = '' + ip route del default gw $(cat ${output s.dhcpv4 "address"}) + echo "0" > /sys/net/ipv4/$(cat ${output s.dhcpv4 "ifname"}) + ''; + }; + systemPackages = [ pkgs.hello ] ; +} diff --git a/make-image.nix b/make-image.nix new file mode 100644 index 0000000..bcd6b48 --- /dev/null +++ b/make-image.nix @@ -0,0 +1,35 @@ +pkgs: config: +let + inherit (pkgs) buildPlatform callPackage runCommandNoCC closureInfo stdenv writeText s6-rc; + + # we need to generate s6 db, by generating closure of all + # config.services and calling s6-rc-compile on them + allServices = closureInfo { + rootPaths = builtins.attrValues config.services; + }; + s6db = stdenv.mkDerivation { + name = "make-s6-db"; + nativeBuildInputs = [pkgs.buildPackages.s6-rc]; + builder = writeText "find-s6-services" '' + source $stdenv/setup + mkdir -p $out + srcs="" + shopt -s nullglob + for i in $(cat ${allServices}/store-paths ); do + if test -d $i; then + for j in $i/* ; do + if test -f $j/type ; then + srcs="$srcs $i" + fi + done + fi + done + echo s6-rc-compile $out/compiled $srcs + s6-rc-compile $out/compiled $srcs + ''; + }; + makeSquashfs = callPackage { + storeContents = [ s6db ] ++ config.packages ; + # comp = "xz -Xdict-size 100%" + }; +in makeSquashfs diff --git a/overlay.nix b/overlay.nix new file mode 100644 index 0000000..d2368b7 --- /dev/null +++ b/overlay.nix @@ -0,0 +1,3 @@ +final: prev: { +# s6-rc = final.callPackage ./pkgs/s6-rc; +} diff --git a/tools/builder.sh b/tools/builder.sh new file mode 100644 index 0000000..04c6104 --- /dev/null +++ b/tools/builder.sh @@ -0,0 +1,16 @@ +source $stdenv/setup +mkdir -p $out/${name} +echo $type > $out/${name}/type +mkdir -p $out/${name}/dependencies.d +echo $buildInputs > $out/buildInputs +test -n "$dependencies" && for d in "$dependencies"; do + touch $out/${name}/dependencies.d/$d +done +test -n "$contents" && for d in "$contents"; do + mkdir -p $out/${name}/contents.d + touch $out/${name}/contents.d/$d +done +test -n "$run" && (echo "$run" > $out/${name}/run) +test -n "$up" && (echo "$up" > $out/${name}/up) +test -n "$down" && (echo "$down" > $out/${name}/down) +(echo $out/${name} && cd $out/${name} && find . -ls) diff --git a/tools/default.nix b/tools/default.nix new file mode 100644 index 0000000..42ebbd4 --- /dev/null +++ b/tools/default.nix @@ -0,0 +1,80 @@ +{ + stdenvNoCC +, s6-rc +} :let + inherit (builtins) concatStringsSep; + longrun = { + name + , run + , outputs ? [] + , dependencies ? [] + } @ args: stdenvNoCC.mkDerivation { + name = "${name}.service"; + type = "longrun"; + buildInputs = dependencies; + dependencies = builtins.map (d: d.name) dependencies; + inherit run; + builder = ./builder.sh; + }; + oneshot = { + name + , up + , down + , outputs ? [] + , dependencies ? [] + , ... + } @ args: stdenvNoCC.mkDerivation { + # stdenvNoCC is to avoid generating derivations with names + # like foo.service-mips-linux-musl + name = "${name}.service"; + type = "oneshot"; + # does this suffice to make sure dependencies are included + # even though the built output has no references to their + # store directories? + buildInputs = dependencies; + inherit up down; + dependencies = builtins.map (d: d.name) dependencies; + builder = ./builder.sh; + }; + bundle = { + name + , contents ? [] + , dependencies ? [] + , ... + }: stdenvNoCC.mkDerivation { + name = "${name}.bundle"; + type = "bundle"; + contents = builtins.map (d: d.name) contents; + buildInputs = dependencies ++ contents; + dependencies = builtins.map (d: d.name) dependencies; + builder = ./builder.sh; + }; +in { + networking = { + interface = { type, device } @ args: oneshot { + name = "${device}.link"; + up = "ip link set up dev ${device}"; + down = "ip link set down dev ${device}"; + } // { + inherit device; + }; + address = interface: { family, addr } @ args: oneshot { + dependencies = [ interface ]; + name = "${interface.device}.addr.${addr}"; + up = "ip address add ${addr} dev ${interface.device} "; + down = "ip address del ${addr} dev ${interface.device} "; + }; + udhcpc = interface: { ... } @ args: longrun { + name = "${interface.device}.udhcp"; + run = "udhchpcd ${interface.device}"; + }; + odhcpc = interface: { ... } @ args: longrun { + name = "${interface.device}.odhcp"; + run = "odhcpcd ${interface.device}"; + }; + }; + services = { + inherit longrun oneshot bundle; + output = service: name: "/run/services/outputs/${service.name}/${name}"; + }; +}