commit 78e4d3012016878370bdd7f1bba3b481218f19cd
parent bb2c36a0b4a92cfbb3f8c3c4b97d89f60c626531
Author: Daniel Barlow <dan@telent.net>
Date: Tue, 4 Nov 2025 00:24:39 +0000
dhcp4 client: start controlled service when lease acquired
it is problematic to have dhcp signal readiness when a lease is
acquired, because it holds the s6 service lock while it's waiting
and that can lead to deadlock. Instead, start the dhcp client
"daemon" process and monitor its outputs, bringing up and down
a controlled "dhcp lease acquired" service that other services
(e.g. ntp, or resolvconf, or ...) can declare as a dependency
Diffstat:
3 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/modules/dhcp4c/client.nix b/modules/dhcp4c/client.nix
@@ -2,6 +2,11 @@
liminix,
writeAshScript,
serviceFns,
+ writeFennel,
+ anoia,
+ linotify,
+ lualinux,
+ s6-rc-up-tree,
lib,
}:
{ interface }:
@@ -29,7 +34,6 @@ let
bound)
# this doesn't actually replace, it adds a new address.
set_address
- # echo >/proc/self/fd/10
;;
renew)
set_address
@@ -39,9 +43,36 @@ let
;;
esac
'';
-in
-longrun {
- inherit name;
- run = "exec /bin/udhcpc -n -A 15 -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
- dependencies = [ interface ];
+ service = longrun {
+ inherit name;
+ run = "exec /bin/udhcpc -n -A 15 -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
+ dependencies = [ interface ];
+ };
+ controlled-name = "${name}-lease-acquired";
+ watcher = longrun {
+ name = "${name}-watcher";
+ dependencies = [ service ];
+ run =
+ let
+ script = writeFennel "dhcp-lease-watcher" {
+ packages = [ anoia linotify lualinux ];
+ mainFunction = "run";
+ }
+ ./lease-watcher.fnl;
+ in ''
+ export PATH=${s6-rc-up-tree}/bin/:$PATH
+ ${script} ${service} ${controlled-name}
+ '';
+ };
+in longrun {
+ name = controlled-name;
+ run = ''
+ set -e
+ echo dhcp lease acquired $(output ${service} ip)
+ (in_outputs ${controlled-name}
+ cp $(output_path ${service})/* .
+ )
+ while sleep 86400 ; do true ; done
+ '';
+ controller = watcher;
}
diff --git a/modules/dhcp4c/lease-watcher.fnl b/modules/dhcp4c/lease-watcher.fnl
@@ -0,0 +1,26 @@
+(local { : %% : system } (require :anoia))
+(local svc (require :anoia.svc))
+
+(fn up-service [name]
+ (system (%% "s6-rc-up-tree %q" name)))
+
+(fn down-service [name]
+ (system (%% "s6-rc -b -d change %q" name)))
+
+(fn react [s controlled]
+ (let [ip (s:output "ip")]
+ (print "event" "ip=" ip)
+ (if ip
+ (up-service controlled)
+ (down-service controlled))))
+
+(fn run []
+ (let [[watched controlled] arg
+ s (assert (svc.open watched))]
+ (print :service s)
+ (react s controlled)
+ (each [e (s:events)]
+ (print :event e)
+ (react s controlled))))
+
+{ : run }
diff --git a/modules/profiles/wap.nix b/modules/profiles/wap.nix
@@ -44,6 +44,7 @@ in
../network
../hostapd
../bridge
+ ../dhcp4c
{ config.services = hostaps; }
];
@@ -57,7 +58,6 @@ in
};
};
config = {
-
services.int = svc.bridge.primary.build {
ifname = "int";
};
@@ -67,7 +67,7 @@ in
members = cfg.interfaces;
};
- services.dhcpc = svc.network.dhcp.client.build {
+ services.dhcpc = svc.dhcp4c.client.build {
interface = config.services.int;
dependencies = [ config.services.hostname ];
};