new package watch-outputs and example of its use

This commit is contained in:
Daniel Barlow 2024-08-14 22:58:17 +01:00
parent 2f82e0dab8
commit d79a941504
17 changed files with 172 additions and 0 deletions

View File

@ -177,6 +177,17 @@ in rec {
};
};
services.restart-on-change = longrun {
name = "wlan0-restart-on-change";
run = ''
${pkgs.watch-outputs}/bin/watch-outputs -r wlan0.link.hostapd ${config.services.secrets} wpa_passphrase
'';
dependencies = [
config.services.hostap-liminix
config.services.hostap-liminix5
];
};
services.bootstrap-dhcpc = svc.network.dhcp.client.build {
interface = config.services.wwan;
dependencies = [ config.services.hostname ];

View File

@ -114,6 +114,7 @@ in {
tufted = callPackage ./tufted { };
uevent-watch = callPackage ./uevent-watch { };
usb-modeswitch = callPackage ./usb-modeswitch { };
watch-outputs = callPackage ./watch-outputs { };
writeAshScript = callPackage ./write-ash-script { };
writeAshScriptBin = callPackage ./write-ash-script/bin.nix { };
writeFennel = callPackage ./write-fennel { };

View File

@ -0,0 +1,3 @@
check:
./output-template '{{' '}}' < example.ini > output
diff -u output example.ini.expected

View File

@ -0,0 +1,35 @@
{
fetchurl,
writeFennel,
fennel,
runCommand,
lua,
anoia,
linotify,
lualinux,
stdenv
}:
let name = "watch-outputs";
in stdenv.mkDerivation {
inherit name;
src = ./.;
buildInputs = [lua];
# doCheck = true;
buildPhase = ''
cp -p ${writeFennel name {
packages = [
anoia
lualinux
linotify
fennel
] ;
mainFunction = "run";
} ./watch-outputs.fnl } ${name}
'';
# checkPhase = "make check";
installPhase = ''
install -D ${name} $out/bin/${name}
'';
}

View File

@ -0,0 +1 @@
a11

View File

@ -0,0 +1 @@
a33

View File

@ -0,0 +1 @@
a55

View File

@ -0,0 +1 @@
a66

View File

@ -0,0 +1 @@
000000

View File

@ -0,0 +1 @@
0000ff

View File

@ -0,0 +1 @@
00ff00

View File

@ -0,0 +1 @@
ff0000

View File

@ -0,0 +1 @@
eth1

View File

@ -0,0 +1,3 @@
wpa_passphrase={{ output("./example-service","colours/black") }}
think = {{ string.format("%q", output("./example-service","colours/blue")) }}
argonaut = {{ json_quote "hello\ngoodbye\tnext\027" }}

View File

@ -0,0 +1,3 @@
wpa_passphrase=000000
think = "0000ff"
argonaut = "hello\ngoodbye\tnext\u001B"

View File

@ -0,0 +1,44 @@
(local svc (require :anoia.svc))
(fn json-escape [s]
;; All Unicode characters may be placed within the quotation marks,
;; except for the characters that MUST be escaped:
;; quotation mark, reverse solidus, and the control characters (U+0000
;; through U+001F). (RFC 8259)
(-> s
(string.gsub
"[\"\b\f\n\r\t]" {
"\b" "\\b"
"\"" "\\\""
"\f" "\\f"
"\n" "\\n"
"\r" "\\r"
"\t" "\\t"
})
(string.gsub
"([\x00-\x1b])"
(fn [x] (string.format "\\u%04X" (string.byte x))))))
(fn substitute [text opening closing]
(let [delim (.. opening "(.-)" closing)
myenv {
: string
:output
(fn [service-path path]
(let [s (assert (svc.open (.. service-path "/.outputs")))]
(s:output path)))
:lua_quote #(string.format "%q" %1)
:json_quote (fn [x] (.. "\"" (json-escape x) "\""))
}]
(string.gsub text delim
(fn [x]
(assert ((load (.. "return " x) x :t myenv))
(string.format "missing value for %q" x))))))
(fn run []
(let [[opening closing] arg
out (substitute (: (io.input) :read "*a") opening closing)]
(io.write out)))
{ : run }

View File

@ -0,0 +1,63 @@
(local { : system : assoc : split : table= } (require :anoia))
(local svc (require :anoia.svc))
(local { : view } (require :fennel))
(local { : kill } (require :lualinux))
(fn split-paths [paths]
(icollect [_ path (ipairs paths)]
(split "/" path)))
(fn parse-args [args]
(match args
["-r" service & rest] (assoc (parse-args rest)
:controlled-service service
:action :restart)
["-R" service & rest] (assoc (parse-args rest)
:controlled-service service
:action :restart-all)
["-s" signal service & rest] (assoc (parse-args rest)
:controlled-service service
:action [:signal signal])
[watched-service & paths] { : watched-service
:paths (split-paths paths)
}))
(fn dig [tree path]
(match path
[el & more] (dig (. tree el) more)
[el] (. tree el)
[] tree))
(fn changed? [paths old-tree new-tree]
(accumulate [changed? false
_ path (ipairs paths)]
(or changed? (not (table= (dig old-tree path) (dig new-tree path))))))
(fn do-action [action service]
(case action
:restart (system "s6-svc -r /run/service/%s" service)
:restart-all (system "s6-rc -b -d %q; s6-rc-up-tree %q" service service)
[:signal n] (system "s6-svc -s %d /run/service/%s" n service)))
(fn run []
(let [{
: controlled-service
: action
: watched-service
: paths } (parse-args arg)
dir (.. watched-service "/.outputs")
_ (print :service-dir dir)
service (assert (svc.open dir))]
(print "watching " watched-service)
(accumulate [tree (service:output ".")
v (service:events)]
(let [new-tree (service:output ".")]
(print :was (view tree) :now (view new-tree))
(when (changed? paths tree new-tree)
(print "watched path event:" action controlled-service)
(do-action action controlled-service))
new-tree))))
{ : run }