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
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
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.
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 = ''
${openwrt.applyPatches.ramips}
${openwrt.applyPatches.rt2x00}
'';
config = {

View File

@ -29,6 +29,10 @@ in {
services = mkOption {
type = types.attrsOf type_service;
};
system.callService = mkOption {
type = types.functionTo (types.functionTo types.anything);
};
filesystem = mkOption {
type = types.anything;
description = ''
@ -111,6 +115,31 @@ in {
"fw_devlink=off"
] ++ 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 = {
uid = 0; gid= 0; gecos = "Root of all evaluation";
dir = "/home/root/";

View File

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

View File

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

View File

@ -37,8 +37,8 @@
(when (not (= up wanted?))
(set up
(if wanted?
(pcall system (.. "s6-rc -u change " service))
(not (pcall system (.. "s6-rc -d change " service)))))
(pcall system (.. "s6-rc -b -u change " service))
(not (pcall system (.. "s6-rc -b -d change " service)))))
))
(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
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)
done

View File

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

View File

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

View File

@ -1,6 +1,7 @@
{
fetchFromGitHub
, writeShellScript
, pkgsBuildBuild
}:
let
src = fetchFromGitHub {
@ -36,4 +37,25 @@ in {
applyPatches.ramips = doPatch "ramips";
applyPatches.mediatek = doPatch "mediatek"; # aarch64
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
for j in $i/* ; do
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"
fi
done
fi
done
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
, writeFennelScript
, lib
, s6-init-bin
, closureInfo
@ -52,7 +53,7 @@ let
chown = if uid>0 || gid>0
then "\nCHOWN(${qpathname},${toString uid},${toString gid});\n"
else "";
in "${cmd} ${chown}";
in "unlink(${qpathname}); ${cmd} ${chown}";
in mapAttrsToList (makeFile prefix) attrset;
activateScript = attrset: writeText "makedevs.c" ''
#include "defs.h"
@ -80,6 +81,7 @@ in attrset:
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-/}

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;
fennel = import ./fennel/test.nix;
tftpboot = import ./tftpboot/test.nix;
updown = import ./updown/test.nix;
}

View File

@ -32,9 +32,5 @@ in {
};
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
# liminix-rebuild calls nix-build so won't run inside a derivation,
# meaning you have to remember to run it manually when changing
# liminix-rebuild
# nix-shell -p expect socat --run "sh ./tests/min-copy-closure/test-liminix-rebuild.sh "
. tests/test-helpers.sh
set -e
while test -n "$1"; do
case $1 in
--fast)
FAST=true
;;
*)
;;
esac
shift
done
here=$(pwd)/tests/min-copy-closure
top=$(pwd)
@ -31,7 +43,7 @@ mkdir ./vm
cat ${rootfs} > rootfs
truncate -s 24M rootfs
truncate -s 32M rootfs
resize2fs rootfs
dd if=rootfs of=disk-image bs=512 seek=4 conv=sync
@ -50,9 +62,12 @@ echo "READY"
touch known_hosts
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

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 {
"# " { send "hostname\r\n" };
}
expect "(none)"
expect {
"(none)" {}
"liminix" {}
timeout { exit(1) }
}

View File

@ -4,4 +4,11 @@
defaultProfile.packages = with pkgs; [
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; };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} address)";
via = "$(output ${services.dhcpv4} ip)";
target = "default";
dependencies = [ services.dhcpv4 ];
};