Compare commits

...

15 Commits

Author SHA1 Message Date
Daniel Barlow a873dc6608 Merge commit 'efcfdcc' 2024-03-28 23:47:04 +00:00
Daniel Barlow 2fb4756a7f add soft restart option to liminix-rebuild
instead of doing a full reboot, it runs activate / and uses
s6-rc-update to install the new service database
2024-03-28 23:45:10 +00:00
Daniel Barlow 04f5174425 fix vanilla-configuration defaultroute 2024-03-28 22:13:21 +00:00
Daniel Barlow dca2e4def1 fix params to s6-rc-init
flags must precede scandir otherwise they're ignored
2024-03-28 21:56:28 +00:00
Daniel Barlow b60126775a improve liminix-rebuild test
* make it executable
* improve robustness
* do't hardcode services.default (why did it do this?)
2024-03-28 21:37:47 +00:00
Daniel Barlow 76f11bcc93 liminix-rebuild: remove -f flag from reboot call
now we have timeouts in service definitions, shouldn't need this
any more
2024-03-28 21:37:47 +00:00
Daniel Barlow efcfdcc21d think 2024-03-28 20:59:39 +00:00
Daniel Barlow 77f1a78331 ifwait block if s6-rc lock is held
otherwise it doesn't trigger the service if something else is
slow to start
2024-03-28 20:59:39 +00:00
Daniel Barlow 28a5dec7dd implement ifwait trigger service and use in bridge
should we convert all ifwait uses to this trigger too? seems
reasonable
2024-03-28 20:59:39 +00:00
Daniel Barlow fad0a47b75 add config.system.callService
this is like pkgs.callService except that it passes
config.system.service as a param so that the service
being defined can invoke other services

if this proves to be a good idea, all uses of
pkgs.callService should be changed to use it instead
2024-03-28 20:59:39 +00:00
Daniel Barlow af52aafc84 deep thoughts 2024-03-28 20:59:39 +00:00
Daniel Barlow 34442b6069 failing test for ifwait 2024-03-28 20:59:39 +00:00
Daniel Barlow b8a46fc05e allow buildInputs param to s6 service
this is in preparation for trigger services that need to
close over the triggered service without adding it to
s6-rc dependencies
2024-03-28 20:58:53 +00:00
Daniel Barlow 8ac2c6cec1 support timeouts (default 30s) for starting s6-rc services 2024-03-28 20:58:47 +00:00
Daniel Barlow 8879b2d1ba fix rt2x00 wifi 2024-03-28 20:58:39 +00:00
28 changed files with 468 additions and 38 deletions

View File

@ -4322,6 +4322,16 @@ set_link virtio-net-pci.0 on
See if both devices are bridge members See if both devices are bridge members
disable again,check if back to starting position
Wed Mar 13 00:00:16 GMT 2024
aside: "trigger" is the least bad word I've thought of so far for
these services that stop/start other services
telent: yeah, in general 'ps afuxww' (or s6-ps -H :)) is the way to solve this, look for hung s6-rc processes and in particular their s6-svlisten1 children, where the command line will show what service is still waiting for readiness
Wed Mar 20 19:34:36 GMT 2024 Wed Mar 20 19:34:36 GMT 2024
Because I forgot hoe to rebuild rotuer, I tihnk it is time to improve Because I forgot hoe to rebuild rotuer, I tihnk it is time to improve
@ -4344,3 +4354,61 @@ just now but it does mean we can punt on specifying the device inside the
liminix-config which is unreasonably circular. liminix-config which is unreasonably circular.
Maybe we'll just chuck a makefile in telent-nixos-config Maybe we'll just chuck a makefile in telent-nixos-config
Fri Mar 22 22:14:32 GMT 2024
For the service failover milestone we said
a. A configuration demonstrating a service which is restarted when it crashes
b. A failover config where service B runs iff service A is unavailable
c. A config showing different pppd behaviour when interface is flakey (retry) vs ppp password is wrong (report error, wait for resolution)
Sun Mar 24 23:41:27 GMT 2024
TODO
1) make liminix-rebuild bounce only affected services instead of
full reboot (what does it do about triggered services?)
2) sniproxy
3) see if arhcive still works. usb disk hotplug would be a good candidate for
switching to triggers
Mon Mar 25 19:35:47 GMT 2024
to make the liminix-rebuild thing restart only affected services, it needs to
know when the new service is not like the old one. By default it does not
restart a service with a changed up/down/run script unless the name has
also changed, so we need to figure out how to generate a "conversion"
file with the services that are different
pkgs/s6-rc-database/default.nix creates $out/compiled, we could add
$out/hashes to this
the other thing making this fun is that we will need to run `activate`
(which is usually done in preinit) otherwise the new configuration's
fhs directories won't exist.
so the plan woyuld be
in liminix-rebuild, when reboot was not chosen,
- run activate
- compare /run/s6-rc/compiled/hashes (old services) with
/etc/s6-rc/compiled/hashes (new services)
- whenever both files have the same column 1 and different
column 2, add that name to restart list
(need to turn restarts.fnl into a lua script)
s6-rc-update /etc/s6-rc/compiled/hashes restarts
Tue Mar 26 23:18:53 GMT 2024
activate overwrites /etc/s6-rc/compiled, which is a problem because
s6-rc-update expects to find the old compiled database here so that
it can know what to update
Maybe config.filesystem should specify /etc/s6-rc/compiled.new
and something in early boot could symlink /etc/s6-rc/compiled to it

View File

@ -136,6 +136,7 @@
}; };
extraPatchPhase = '' extraPatchPhase = ''
${openwrt.applyPatches.ramips} ${openwrt.applyPatches.ramips}
${openwrt.applyPatches.rt2x00}
''; '';
config = { config = {

View File

@ -29,6 +29,10 @@ in {
services = mkOption { services = mkOption {
type = types.attrsOf type_service; type = types.attrsOf type_service;
}; };
system.callService = mkOption {
type = types.functionTo (types.functionTo types.anything);
};
filesystem = mkOption { filesystem = mkOption {
type = types.anything; type = types.anything;
description = '' description = ''
@ -111,6 +115,31 @@ in {
"fw_devlink=off" "fw_devlink=off"
] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}"; ] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}";
system.callService = path : parameters :
let
typeChecked = caller: type: value:
let
inherit (lib) types mergeDefinitions;
defs = [{ file = caller; inherit value; }];
type' = types.submodule { options = type; };
in (mergeDefinitions [] type' defs).mergedValue;
cp = lib.callPackageWith(pkgs // { svc = config.system.service; });
pkg = cp path {};
checkTypes = t : p : typeChecked (builtins.toString path) t p;
in {
inherit parameters;
build = { dependencies ? [], ... } @ args :
let
s = pkg (checkTypes parameters
(builtins.removeAttrs args ["dependencies"]));
in s.overrideAttrs (o: {
dependencies = (builtins.map (d: d.name) dependencies) ++ o.dependencies;
buildInputs = dependencies ++ o.buildInputs;
});
};
users.root = { users.root = {
uid = 0; gid= 0; gecos = "Root of all evaluation"; uid = 0; gid= 0; gecos = "Root of all evaluation";
dir = "/home/root/"; dir = "/home/root/";

View File

@ -14,6 +14,8 @@ let
inherit (pkgs) liminix; inherit (pkgs) liminix;
in in
{ {
imports = [ ../ifwait ];
options = { options = {
system.service.bridge = { system.service.bridge = {
primary = mkOption { type = liminix.lib.types.serviceDefn; }; primary = mkOption { type = liminix.lib.types.serviceDefn; };
@ -27,7 +29,7 @@ in
description = "bridge interface name to create"; description = "bridge interface name to create";
}; };
}; };
members = liminix.callService ./members.nix { members = config.system.callService ./members.nix {
primary = mkOption { primary = mkOption {
type = liminix.lib.types.interface; type = liminix.lib.types.interface;
description = "primary bridge interface"; description = "primary bridge interface";
@ -47,5 +49,5 @@ in
# a better way to test for the existence of vlan config: # a better way to test for the existence of vlan config:
# maybe the module should set an `enabled` attribute? # maybe the module should set an `enabled` attribute?
BRIDGE_VLAN_FILTERING = "y"; BRIDGE_VLAN_FILTERING = "y";
}; };
} }

View File

@ -2,6 +2,7 @@
liminix liminix
, ifwait , ifwait
, lib , lib
, svc
}: }:
{ members, primary } : { members, primary } :
@ -10,14 +11,20 @@ let
inherit (liminix.services) bundle oneshot; inherit (liminix.services) bundle oneshot;
inherit (lib) mkOption types; inherit (lib) mkOption types;
addif = member : addif = member :
oneshot { # how do we get sight of services from here? maybe we need to
name = "${primary.name}.member.${member.name}"; # implement ifwait as a regualr derivation instead of a
up = '' # servicedefinition
dev=$(output ${member} ifname) svc.ifwait.build {
${ifwait}/bin/ifwait $dev running && ip link set dev $dev master $(output ${primary} ifname) state = "running";
''; interface = member;
down = "ip link set dev $(output ${member} ifname) nomaster";
dependencies = [ primary member ]; dependencies = [ primary member ];
service = oneshot {
name = "${primary.name}.member.${member.name}";
up = ''
ip link set dev $(output ${member} ifname) master $(output ${primary} ifname)
'';
down = "ip link set dev $(output ${member} ifname) nomaster";
};
}; };
in bundle { in bundle {
name = "${primary.name}.members"; name = "${primary.name}.members";

View File

@ -0,0 +1,18 @@
{ config, pkgs, lib, ... } :
let
inherit (pkgs) liminix;
inherit (lib) mkOption types;
in {
options.system.service.ifwait =
mkOption { type = liminix.lib.types.serviceDefn; };
config.system.service.ifwait = config.system.callService ./ifwait.nix {
state = mkOption { type = types.str; };
interface = mkOption {
type = liminix.lib.types.interface;
};
service = mkOption {
type = liminix.lib.types.service;
};
};
}

15
modules/ifwait/ifwait.nix Normal file
View File

@ -0,0 +1,15 @@
{ ifwait, liminix } :
{
state
, interface
, service
}:
let
inherit (liminix.services) longrun;
in longrun {
name = "ifwait.${interface.name}";
buildInputs = [ service ];
run = ''
${ifwait}/bin/ifwait -s ${service.name} $(output ${interface} ifname) ${state}
'';
}

View File

@ -34,7 +34,7 @@ fi
### If your services are managed by s6-rc: ### If your services are managed by s6-rc:
### (replace /run/service with your scandir) ### (replace /run/service with your scandir)
s6-rc-init /run/service -d -c /etc/s6-rc/compiled s6-rc-init -d -c /etc/s6-rc/compiled /run/service
### 2. Starting the wanted set of services ### 2. Starting the wanted set of services

View File

@ -37,8 +37,8 @@
(when (not (= up wanted?)) (when (not (= up wanted?))
(set up (set up
(if wanted? (if wanted?
(pcall system (.. "s6-rc -u change " service)) (pcall system (.. "s6-rc -b -u change " service))
(not (pcall system (.. "s6-rc -d change " service))))) (not (pcall system (.. "s6-rc -b -d change " service)))))
)) ))
(fn run [args event-fn] (fn run [args event-fn]

View File

@ -11,7 +11,7 @@ test -n "$contents" && for d in $contents; do
touch $out/${name}/contents.d/$d touch $out/${name}/contents.d/$d
done done
for i in run notification-fd up down consumer-for producer-for pipeline-name ; do for i in timeout-up timeout-down run notification-fd up down consumer-for producer-for pipeline-name ; do
test -n "$(printenv $i)" && (echo "$(printenv $i)" > $out/${name}/$i) test -n "$(printenv $i)" && (echo "$(printenv $i)" > $out/${name}/$i)
done done

View File

@ -31,6 +31,8 @@ let
, producer-for ? null , producer-for ? null
, consumer-for ? null , consumer-for ? null
, pipeline-name ? null , pipeline-name ? null
, timeout-up ? 30000 # milliseconds
, timeout-down ? 0
, dependencies ? [] , dependencies ? []
, contents ? [] , contents ? []
, buildInputs ? [] , buildInputs ? []
@ -39,7 +41,7 @@ let
# we use stdenvNoCC to avoid generating derivations with names # we use stdenvNoCC to avoid generating derivations with names
# like foo.service-mips-linux-musl # like foo.service-mips-linux-musl
inherit name serviceType up down run notification-fd inherit name serviceType up down run notification-fd
producer-for consumer-for pipeline-name; producer-for consumer-for pipeline-name timeout-up timeout-down;
buildInputs = buildInputs ++ dependencies ++ contents; buildInputs = buildInputs ++ dependencies ++ contents;
dependencies = builtins.map (d: d.name) dependencies; dependencies = builtins.map (d: d.name) dependencies;
contents = builtins.map (d: d.name) contents; contents = builtins.map (d: d.name) contents;
@ -52,6 +54,7 @@ let
, outputs ? [] , outputs ? []
, notification-fd ? null , notification-fd ? null
, dependencies ? [] , dependencies ? []
, buildInputs ? []
, ... , ...
} @ args: } @ args:
let logger = service { let logger = service {
@ -63,7 +66,7 @@ let
pipeline-name = "${name}-pipeline"; pipeline-name = "${name}-pipeline";
}; };
in service (args // { in service (args // {
buildInputs = [ logger ]; buildInputs = buildInputs ++ [ logger ];
serviceType = "longrun"; serviceType = "longrun";
run = serviceScript "${run}\n${cleanupScript name}"; run = serviceScript "${run}\n${cleanupScript name}";
producer-for = "${name}-log"; producer-for = "${name}-log";

View File

@ -1,12 +1,19 @@
#!/usr/bin/env bash #!/usr/bin/env bash
ssh_command=${SSH_COMMAND-ssh} ssh_command=${SSH_COMMAND-ssh}
if [ "$1" = "--no-reboot" ] ; then
reboot="true" reboot="reboot"
shift
else case "$1" in
reboot="reboot" "--no-reboot")
fi unset reboot
shift
;;
"--fast")
reboot="soft"
shift
;;
esac
target_host=$1 target_host=$1
shift shift
@ -20,7 +27,16 @@ if toplevel=$(nix-build "$@" -A outputs.systemConfiguration --no-out-link); then
echo systemConfiguration $toplevel echo systemConfiguration $toplevel
min-copy-closure $target_host $toplevel min-copy-closure $target_host $toplevel
$ssh_command $target_host $toplevel/bin/install $ssh_command $target_host $toplevel/bin/install
$ssh_command $target_host "sync; source /etc/profile; reboot -f" case "$reboot" in
reboot)
$ssh_command $target_host "sync; source /etc/profile; reboot"
;;
soft)
$ssh_command $target_host $toplevel/bin/restart-services
;;
*)
;;
esac
else else
echo Rebuild failed echo Rebuild failed
fi fi

View File

@ -1,6 +1,7 @@
{ {
fetchFromGitHub fetchFromGitHub
, writeShellScript , writeShellScript
, pkgsBuildBuild
}: }:
let let
src = fetchFromGitHub { src = fetchFromGitHub {
@ -36,4 +37,25 @@ in {
applyPatches.ramips = doPatch "ramips"; applyPatches.ramips = doPatch "ramips";
applyPatches.mediatek = doPatch "mediatek"; # aarch64 applyPatches.mediatek = doPatch "mediatek"; # aarch64
applyPatches.mvebu = doPatch "mvebu"; # arm applyPatches.mvebu = doPatch "mvebu"; # arm
applyPatches.rt2x00 = ''
PATH=${pkgsBuildBuild.patchutils}/bin:$PATH
for i in ${src}/package/kernel/mac80211/patches/rt2x00/6*.patch ; do
fixed=$(basename $i).fixed
sed '/depends on m/d' < $i | sed 's/CPTCFG_/CONFIG_/g' | recountdiff | filterdiff -x '*/local-symbols' > $fixed
case $fixed in
606-*)
;;
611-*)
filterdiff -x '*/rt2x00.h' < $fixed | patch --forward -p1
;;
601-*|607-*)
filterdiff -x '*/rt2x00_platform.h' < $fixed | patch --forward -p1
;;
*)
cat $fixed | patch --forward -p1
;;
esac
done
'';
} }

View File

@ -21,11 +21,22 @@ in stdenvNoCC.mkDerivation {
if test -d $i; then if test -d $i; then
for j in $i/* ; do for j in $i/* ; do
if test -f $j/type ; then if test -f $j/type ; then
case $(cat $j/type) in
longrun|oneshot)
# s6-rc-update only wants oneshots in its
# restarts file
echo $(basename $j) " " $i >> $out/hashes
;;
*)
;;
esac
srcs="$srcs $i" srcs="$srcs $i"
fi fi
done done
fi fi
done done
s6-rc-compile $out/compiled $srcs s6-rc-compile $out/compiled $srcs
''; s6-rc-db -c $out/compiled contents default
} mv $out/hashes $out/compiled
'';
}

View File

@ -6,6 +6,7 @@
{ {
writeText writeText
, writeFennelScript
, lib , lib
, s6-init-bin , s6-init-bin
, closureInfo , closureInfo
@ -52,7 +53,7 @@ let
chown = if uid>0 || gid>0 chown = if uid>0 || gid>0
then "\nCHOWN(${qpathname},${toString uid},${toString gid});\n" then "\nCHOWN(${qpathname},${toString uid},${toString gid});\n"
else ""; else "";
in "${cmd} ${chown}"; in "unlink(${qpathname}); ${cmd} ${chown}";
in mapAttrsToList (makeFile prefix) attrset; in mapAttrsToList (makeFile prefix) attrset;
activateScript = attrset: writeText "makedevs.c" '' activateScript = attrset: writeText "makedevs.c" ''
#include "defs.h" #include "defs.h"
@ -80,6 +81,7 @@ in attrset:
cp $closure/store-paths $out/etc/nix-store-paths cp $closure/store-paths $out/etc/nix-store-paths
$STRIP --remove-section=.note --remove-section=.comment --strip-all makedevs -o $out/bin/activate $STRIP --remove-section=.note --remove-section=.comment --strip-all makedevs -o $out/bin/activate
ln -s ${s6-init-bin}/bin/init $out/bin/init 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 cat > $out/bin/install <<EOF
#!/bin/sh -e #!/bin/sh -e
prefix=\''${1-/} prefix=\''${1-/}

View File

@ -0,0 +1,37 @@
(fn hashes-from-file [name]
(with-open [f (assert (io.open name :r) name)]
(accumulate [h []
l #(f:read "*l")]
(let [(name hash) (string.match l "([^%s]+) +([^%s]+)")]
(if name
(doto h (tset name hash))
h)))))
(fn write-restarts [old new]
(let [old-hashes (hashes-from-file old)
new-hashes (hashes-from-file new)]
(with-open [f (io.open "/tmp/restarts" :w)]
(each [n h (pairs old-hashes)]
(when (not (= h (. new-hashes n)))
(f:write (.. n " restart\n")))))))
(fn exec [text command]
(io.write (.. text ": "))
(match (os.execute command)
res (print "[OK]")
(nil err) (error (.. "[FAILED " err "]"))))
(let [mypath (: (. arg 0) :match "(.*/)")
activate (.. mypath "activate /")
old-compiled "/run/s6-rc/compiled/"
new-compiled "/etc/s6-rc/compiled/"]
(exec "installing FHS files" activate)
(write-restarts (.. old-compiled "hashes") (.. new-compiled "hashes"))
(exec "updating service database"
(.. "s6-rc-update -f /tmp/restarts " new-compiled))
(exec "starting services" (.. "s6-rc -u -p change default"))
)

View File

@ -0,0 +1,12 @@
loopback /nix/store/3k7yp08465k8z8a1514slansj0hbrvbq-loopback
default /nix/store/5247rr0mcr17x9lkaqjiywxmn4q3ibgd-default
lan.link.a.10.3.0.1 /nix/store/99ic5bryzbx12ip4v8sh9cdrv15c8qwl-lan.link.a.10.3.0.1
lo.link.a.-1 /nix/store/a2yvj42ghcg8iyvi51zcmzmpwwkrx30l-lo.link.a.-1
sshd /nix/store/afgznmavi6s9dxkv4vnn9xwd7qz5kk35-sshd
lo.link /nix/store/dz0nvlf16lqv9yndas0sk0xqh7jvx8gm-lo.link
lo.link.a.127.0.0.1 /nix/store/jl018kbgxcnyb70s0cfw19q1zqa0pval-lo.link.a.127.0.0.1
lan.link /nix/store/lxvmhb7ax46s2akj9lsivcq494kmx0hm-lan.link
lan.link.a.10.3.0.1.dnsmasq-log /nix/store/rk8265ikd9lj3fpibyrw0kal32h0ksqg-lan.link.a.10.3.0.1.dnsmasq-log
hostname /nix/store/xfy0ymvnd0zv6wcd26h405f85hfkd8wq-hostname
sshd-log /nix/store/xj58nxvkacsjg3rvwb4bxcqnhaav4d3s-sshd-log
lan.link.a.10.3.0.1.dnsmasq /nix/store/z4l7h9pr4an9aq921cs9wrlvf9b8xr48-lan.link.a.10.3.0.1.dnsmasq

View File

@ -0,0 +1,12 @@
loopback /nix/store/3k7yp08465k8z8a1514slansj0hbrvbq-loopback
default /nix/store/5247rr0mcr17x9lkaqjiywxmn4q3ibgd-default
lan.link.a.10.3.0.1 /nix/store/99ic5bryzbx12ip4v8sh9cdrv15c8qwl-lan.link.a.10.3.0.1
lo.link.a.-1 /nix/store/a2yvj42ghcg8iyvi51zcmzmpwwkrx30l-lo.link.a.-1
sshd /nix/store/afgznmavi6s9dxkv4vnn9xwd7qz5kk35-sshd
lo.link /nax/store/dz0nvlf16lqv9yndas0sk0xqh7jvx8gm-lo.link
lo.link.a.127.0.0.1 /nix/store/jl018kbgxcnyb70s0cfw19q1zqa0pval-lo.link.a.127.0.0.1
lan.link /nix/store/lxvmhb7ax46s2akj9lsivcq494kmx0hm-lan.link
lan.link.a.10.3.0.1.dnsmasq-log /nix/store/rk8265ikd9lj3fpibyrw0kal32h0ksqg-lan.link.a.10.3.0.1.dnsmasq-log
hostname /nax/store/xfy0ymvnd0zv6wcd26h405f85hfkd8wq-hostname
sshd-log /nax/store/xj58nxvkacsjg3rvwb4bxcqnhaav4d3s-sshd-log

View File

@ -8,4 +8,5 @@
min-copy-closure = import ./min-copy-closure/test.nix; min-copy-closure = import ./min-copy-closure/test.nix;
fennel = import ./fennel/test.nix; fennel = import ./fennel/test.nix;
tftpboot = import ./tftpboot/test.nix; tftpboot = import ./tftpboot/test.nix;
updown = import ./updown/test.nix;
} }

View File

@ -32,9 +32,5 @@ in {
}; };
rootfsType = "jffs2"; rootfsType = "jffs2";
services.default = lib.mkForce (target {
name = "default";
contents = with config.services; [ loopback ntp defaultroute4 sshd dhcpv4 ];
});
}; };
} }

29
tests/min-copy-closure/test-liminix-rebuild.sh Normal file → Executable file
View File

@ -1,14 +1,26 @@
#!/usr/bin/env nix-shell
#! nix-shell -v -i bash -p expect socat
# This is a test for liminix-rebuild. It's not a CI test because # This is a test for liminix-rebuild. It's not a CI test because
# liminix-rebuild calls nix-build so won't run inside a derivation, # liminix-rebuild calls nix-build so won't run inside a derivation,
# meaning you have to remember to run it manually when changing # meaning you have to remember to run it manually when changing
# liminix-rebuild # liminix-rebuild
# nix-shell -p expect socat --run "sh ./tests/min-copy-closure/test-liminix-rebuild.sh "
. tests/test-helpers.sh . tests/test-helpers.sh
set -e set -e
while test -n "$1"; do
case $1 in
--fast)
FAST=true
;;
*)
;;
esac
shift
done
here=$(pwd)/tests/min-copy-closure here=$(pwd)/tests/min-copy-closure
top=$(pwd) top=$(pwd)
@ -31,7 +43,7 @@ mkdir ./vm
cat ${rootfs} > rootfs cat ${rootfs} > rootfs
truncate -s 24M rootfs truncate -s 32M rootfs
resize2fs rootfs resize2fs rootfs
dd if=rootfs of=disk-image bs=512 seek=4 conv=sync dd if=rootfs of=disk-image bs=512 seek=4 conv=sync
@ -50,9 +62,12 @@ echo "READY"
touch known_hosts touch known_hosts
export SSH_COMMAND="ssh -o UserKnownHostsFile=${work}/known_hosts -o StrictHostKeyChecking=no -p 2022 -i ${here}/id" export SSH_COMMAND="ssh -o UserKnownHostsFile=${work}/known_hosts -o StrictHostKeyChecking=no -p 2022 -i ${here}/id"
(cd ${top} && liminix-rebuild root@localhost -I liminix-config=${here}/with-figlet.nix --arg device "import ./devices/qemu-armv7l") if test -n "$FAST"; then
(cd ${top} && liminix-rebuild --fast root@localhost -I liminix-config=${here}/with-figlet.nix --argstr deviceName qemu-armv7l)
cd ${work} && expect $here/wait-for-soft-restart.expect
else
(cd ${top} && liminix-rebuild root@localhost -I liminix-config=${here}/with-figlet.nix --arg device "import ./devices/qemu-armv7l")
cd ${work} && expect $here/wait-for-reboot.expect
fi
ls -l vm
cd ${work} && expect $here/wait-for-reboot.expect
cd / ; rm -rf $work cd / ; rm -rf $work

View File

@ -0,0 +1,13 @@
proc chat {instr outstr} {
expect {
$instr { send $outstr }
timeout { exit 1 }
}
}
spawn socat unix-connect:vm/console -
send "exit\r"
chat "BusyBox" "\r"
chat "#" "stty -echo; type -p figlet\r"
chat "figlet-armv7l-unknown-linux" "s6-rc -a list |grep w\\inkle\r"
chat "winkle" "poweroff\r"

View File

@ -5,4 +5,9 @@ send "\r\n"
expect { expect {
"# " { send "hostname\r\n" }; "# " { send "hostname\r\n" };
} }
expect "(none)"
expect {
"(none)" {}
"liminix" {}
timeout { exit(1) }
}

View File

@ -4,4 +4,11 @@
defaultProfile.packages = with pkgs; [ defaultProfile.packages = with pkgs; [
figlet figlet
]; ];
services.ripvanwinkle = pkgs.liminix.services.longrun {
name = "winkle";
run = ''
echo SLEEPING > /dev/console
sleep 3600
'';
};
} }

View File

@ -0,0 +1,55 @@
{ config, pkgs, lib, ... } :
let
inherit (pkgs.liminix.services) bundle oneshot longrun;
inherit (pkgs) serviceFns;
# EDIT: you can pick your preferred RFC1918 address space
# for NATted connections, if you don't like this one.
ipv4LocalNet = "10.8.0";
svc = config.system.service;
in rec {
imports = [
../../modules/bridge
../../modules/dhcp6c
../../modules/dnsmasq
../../modules/firewall
../../modules/hostapd
../../modules/network
../../modules/ssh
../../modules/vlan
../../modules/wlan.nix
];
rootfsType = "jffs2";
hostname = "updown";
services.int = svc.network.address.build {
interface = svc.bridge.primary.build { ifname = "int"; };
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces;
[ lan ];
};
services.sshd = svc.ssh.build { };
# users.root = {
# # EDIT: choose a root password and then use
# # "mkpasswd -m sha512crypt" to determine the hash.
# # It should start wirh $6$.
# passwd = "$6$6HG7WALLQQY1LQDE$428cnouMJ7wVmyK9.dF1uWs7t0z9ztgp3MHvN5bbeo0M4Kqg/u2ThjoSHIjCEJQlnVpDOaEKcOjXAlIClHWN21";
# openssh.authorizedKeys.keys = [
# # EDIT: you can add your ssh pubkey here
# # "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
# ];
# };
defaultProfile.packages = with pkgs; [
min-collect-garbage
# strace
# ethtool
tcpdump
];
}

View File

@ -0,0 +1,64 @@
set timeout 10
spawn socat unix-connect:vm/monitor -
set monitor_id $spawn_id
expect "(qemu)"
send "set_link virtio-net-pci.1 off\n"
expect "(qemu)"
send "set_link virtio-net-pci.0 off\n"
expect "(qemu)"
send "c\r\n"
spawn socat unix-connect:vm/console -
set console_id $spawn_id
expect "BusyBox"
expect "#" { send "PS1=RE\\ADY_\\ ; stty -echo \r" }
expect "READY_" { send "s6-rc -b -a list\r" } ; # -b waits for s6-rc lock
expect "READY_" { send "ls /sys/class/net/lan/master\r" }
expect {
"No such file or directory" { }
timeout { exit 1 }
}
expect "READY_" { send "cat /sys/class/net/lan/operstate\r" }
expect {
"down" { }
"up" { exit 1 }
}
expect "READY_" { send "s6-rc -a -u change\r" }
expect {
"unable to take locks" { exit 1 }
"READY_" { send "\r" }
}
set spawn_id $monitor_id
send "\r"
expect "(qemu)"
send "set_link virtio-net-pci.1 on\n"
expect "(qemu)"
send "set_link virtio-net-pci.0 on\n"
expect "(qemu)"
set spawn_id $console_id
expect "entered forwarding state"
send "\r"
expect "READY_" { send "cat /sys/class/net/lan/operstate\r" }
expect {
"down" { exit 1 }
"up" { }
}
expect "READY_" { send "cat /sys/class/net/lan/master/uevent\r" }
expect {
"INTERFACE=int" { }
timeout { exit 1 }
}
expect "READY_" { send "s6-rc listall int.link.a.10.8.0.1.member.lan.link ; hostname\r" }
expect {
"updown" {}
timeout { exit 1 }
}

19
tests/updown/test.nix Normal file
View File

@ -0,0 +1,19 @@
{
liminix
, nixpkgs
}:
let img = (import liminix {
device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix;
}).outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [
expect
socat
] ;
} ''
mkdir vm
${img}/run.sh --flag -S --background ./vm
expect ${./script.expect} | tee $out
''

View File

@ -16,7 +16,7 @@ in rec {
in svc.network.dhcp.client.build { interface = iface; }; in svc.network.dhcp.client.build { interface = iface; };
services.defaultroute4 = svc.network.route.build { services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} address)"; via = "$(output ${services.dhcpv4} ip)";
target = "default"; target = "default";
dependencies = [ services.dhcpv4 ]; dependencies = [ services.dhcpv4 ];
}; };