Compare commits
6 Commits
adf62d4483
...
771585546d
Author | SHA1 | Date |
---|---|---|
Daniel Barlow | 771585546d | |
Daniel Barlow | 73abf952d5 | |
Daniel Barlow | 8af4e9fd5b | |
Daniel Barlow | 7e19d80130 | |
Daniel Barlow | 0f0688c802 | |
Daniel Barlow | b43f17f655 |
113
THOUGHTS.txt
113
THOUGHTS.txt
|
@ -4563,3 +4563,116 @@ plan:
|
||||||
introduce uevent-watcher command, update test to use it
|
introduce uevent-watcher command, update test to use it
|
||||||
|
|
||||||
make mount service use it
|
make mount service use it
|
||||||
|
|
||||||
|
Tue Apr 16 18:59:25 BST 2024
|
||||||
|
|
||||||
|
Another idea for maybe-not-now: tftp local/peer addresses could be
|
||||||
|
provided as top-level params (e.g. to nix-build).
|
||||||
|
|
||||||
|
Wed Apr 17 18:57:49 BST 2024
|
||||||
|
|
||||||
|
I hatched a plan (and forgot to save this file) to build a service
|
||||||
|
that subscribes to uevents and retains state so that other services
|
||||||
|
can know about things that happened before they started. I'm wondering
|
||||||
|
if it's really needed though, because there could be one process to
|
||||||
|
read the socket and start/stop *all* the udev triggered services. Not
|
||||||
|
sure how we'd describe this in nix though: how do all the other
|
||||||
|
services
|
||||||
|
|
||||||
|
How we would do a uevent database service (sysfsq):
|
||||||
|
|
||||||
|
for each event e from socket
|
||||||
|
|
||||||
|
if e.action in (add, change)
|
||||||
|
path[e.path] = e.attribues
|
||||||
|
|
||||||
|
if e.action == 'remove'
|
||||||
|
path.remove e.path
|
||||||
|
|
||||||
|
(update-indices e)
|
||||||
|
|
||||||
|
|
||||||
|
(fn update-indices [event]
|
||||||
|
for each k in (keys event)
|
||||||
|
index.k.v += e)
|
||||||
|
|
||||||
|
we also want to not maintain indexes when there are so many values in
|
||||||
|
the index entry to make searching it worthless.
|
||||||
|
|
||||||
|
to retrieve, look at each criterion that has an index and choose the
|
||||||
|
index with fewest elements in the value. scan that index for the other
|
||||||
|
criteria
|
||||||
|
|
||||||
|
there are 813 uevent files in sysfs on arhcive, is this all overkill?
|
||||||
|
maybe we could simplify using a hardcoded stopword list - e.g. don't
|
||||||
|
have indices for MAJOR, MINOR
|
||||||
|
|
||||||
|
what are we going to use for querying? can't be netlink because that's
|
||||||
|
a shared medium (broadcast/multicast). unix dgram socket? alternative
|
||||||
|
would be to somehow use the filesystem as a database
|
||||||
|
|
||||||
|
Wed Apr 17 22:00:29 BST 2024
|
||||||
|
|
||||||
|
tests. assuming the sysfs setup from all-events.txt, we can write tests lik
|
||||||
|
|
||||||
|
- there is a path for $foo
|
||||||
|
- the attributes are x, y, z
|
||||||
|
|
||||||
|
- when I add a device with $attributes, I can recall it
|
||||||
|
- by path
|
||||||
|
- by attribute value
|
||||||
|
|
||||||
|
- when I remove it again, I cannot access it by path or attributes
|
||||||
|
|
||||||
|
- when I add a device with $attributes major minor foo bar baz
|
||||||
|
it is added to indices for foo bar baz but not major minor
|
||||||
|
|
||||||
|
- when I remove it, it can no longer be found by looking in any index
|
||||||
|
|
||||||
|
- when I query with multiple attributes, the search is performed
|
||||||
|
using the most specific attribute (= the attribute whose
|
||||||
|
value at this key has fewest elements)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
I am still looking for ways to avoid doing this, but it is potentially
|
||||||
|
the first of several "database" services that triggers could want to
|
||||||
|
use so maybe it's an emerging pattern.
|
||||||
|
|
||||||
|
|
||||||
|
https://github.com/philanc/minisock useful? we could almost replace
|
||||||
|
nellie with it only not quite (it hardcodes 0 as the "protocol" param
|
||||||
|
to socket())
|
||||||
|
|
||||||
|
Fri Apr 19 20:55:22 BST 2024
|
||||||
|
|
||||||
|
We could have a service that's present only when a devdb entry is
|
||||||
|
present. For example mount_disk only runs when partlabel=foo
|
||||||
|
|
||||||
|
Or we could have a service that continues to run as the $somedatabase
|
||||||
|
service state changes and does different things depending on the
|
||||||
|
nature of those changes. For example, [I can't think of an example
|
||||||
|
now, but it was definitely an issue the other day, maybe I dreamt it]
|
||||||
|
I don't think this will be such an issue for devdb becuase there isn't
|
||||||
|
much in it that has continuously varying values. Maybe battery health
|
||||||
|
is the exception there
|
||||||
|
|
||||||
|
The step ahead we're thinking here is: how do clients do a request? A
|
||||||
|
single one-of request for state is fine but chances are that a client
|
||||||
|
will do that to get initial state and then need to open a netlink
|
||||||
|
socket to get updates: well, if we can feed them the initial state
|
||||||
|
filtered for their needs why can't we send them the relevant updates
|
||||||
|
as well? This makes the database server design a bit more complicated
|
||||||
|
as it needs to remember each client and their subscriptions, and then
|
||||||
|
send only relevant updates to each subscribed client
|
||||||
|
|
||||||
|
* should a client be allowed multiple subscriptions on the same
|
||||||
|
connection?
|
||||||
|
|
||||||
|
* do we guarantee that every message sent is matching the subscription
|
||||||
|
or can we send other stuff as well if it makes implementation easier?
|
||||||
|
it might defeat the purpose a bit because it means the client also
|
||||||
|
needs to filter, but the client will anyway have to do some message
|
||||||
|
parsing so they can distinguish add from remove
|
||||||
|
|
||||||
|
* where do we start?
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
(local subject (require :acquire-wan-address))
|
(local subject (require :acquire-wan-address))
|
||||||
(local { : view } (require :fennel))
|
(import-macros { : expect= } :anoia.assert)
|
||||||
(local { : merge : dup } (require :anoia))
|
(local { : merge : dup } (require :anoia))
|
||||||
|
|
||||||
|
;; nix-shell --run "cd modules/dhcp6c && fennelrepl acquire-wan-address-test.fnl"
|
||||||
|
|
||||||
(local a1
|
(local a1
|
||||||
{
|
{
|
||||||
|
@ -47,19 +48,6 @@
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
(macro expect [assertion]
|
|
||||||
(let [msg (.. "expectation failed: " (view assertion))]
|
|
||||||
`(when (not ,assertion)
|
|
||||||
(assert false ,msg))))
|
|
||||||
|
|
||||||
(macro expect= [actual expected]
|
|
||||||
`(let [ve# (view ,expected)
|
|
||||||
va# (view ,actual)]
|
|
||||||
(when (not (= ve# va#))
|
|
||||||
(assert false
|
|
||||||
(.. "\nexpected " ve# "\ngot " va#)
|
|
||||||
))))
|
|
||||||
|
|
||||||
(fn first-address []
|
(fn first-address []
|
||||||
(let [deleted
|
(let [deleted
|
||||||
(subject.deletions
|
(subject.deletions
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
default: fs.lua init.lua nl.lua svc.lua
|
||||||
|
|
||||||
|
test:
|
||||||
|
fennel test.fnl
|
||||||
|
|
||||||
|
%.lua: %.fnl
|
||||||
|
fennel --compile $< > $@
|
|
@ -0,0 +1,21 @@
|
||||||
|
;; these are macros; this module should be imported
|
||||||
|
;; using import-macros
|
||||||
|
|
||||||
|
;; e.g. (import-macros { : expect= } :anoia.assert)
|
||||||
|
|
||||||
|
|
||||||
|
(fn expect [assertion]
|
||||||
|
(let [msg (.. "expectation failed: " (view assertion))]
|
||||||
|
`(when (not ,assertion)
|
||||||
|
(assert false ,msg))))
|
||||||
|
|
||||||
|
(fn expect= [actual expected]
|
||||||
|
`(let [view# (. (require :fennel) :view)
|
||||||
|
ve# (view# ,expected)
|
||||||
|
va# (view# ,actual)]
|
||||||
|
(when (not (= ve# va#))
|
||||||
|
(assert false
|
||||||
|
(.. "\nexpected " ve# "\ngot " va#)
|
||||||
|
))))
|
||||||
|
|
||||||
|
{ : expect : expect= }
|
|
@ -10,13 +10,15 @@ in stdenv.mkDerivation {
|
||||||
src = ./.;
|
src = ./.;
|
||||||
nativeBuildInputs = [ fennel ];
|
nativeBuildInputs = [ fennel ];
|
||||||
buildInputs = with lua.pkgs; [ luafilesystem ];
|
buildInputs = with lua.pkgs; [ luafilesystem ];
|
||||||
buildPhase = ''
|
outputs = [ "out" "dev" ];
|
||||||
for f in *.fnl ; do
|
|
||||||
fennel --compile $f > `basename $f .fnl`.lua
|
doCheck = true;
|
||||||
done
|
|
||||||
'';
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p "$out/share/lua/${lua.luaversion}/${pname}"
|
mkdir -p "$out/share/lua/${lua.luaversion}/${pname}"
|
||||||
cp *.lua "$out/share/lua/${lua.luaversion}/${pname}"
|
cp *.lua "$out/share/lua/${lua.luaversion}/${pname}"
|
||||||
|
|
||||||
|
mkdir -p "$dev/share/lua/${lua.luaversion}/${pname}"
|
||||||
|
cp assert.fnl "$dev/share/lua/${lua.luaversion}/${pname}"
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
(local { : hash : base64url } (require :anoia))
|
(local { : hash : base64url } (require :init))
|
||||||
|
(import-macros { : expect= } :assert)
|
||||||
|
|
||||||
(assert (= (hash "") 5381))
|
(expect= (hash "") 5381)
|
||||||
|
|
||||||
;; these examples from https://theartincode.stanis.me/008-djb2/
|
;; these examples from https://theartincode.stanis.me/008-djb2/
|
||||||
(assert (= (hash "Hello") 210676686969))
|
(expect= (hash "Hello") 210676686969)
|
||||||
(assert (= (hash "Hello!") 6952330670010))
|
(expect= (hash "Hello!") 6952330670010)
|
||||||
|
|
||||||
(assert (= (base64url "hello world") "aGVsbG8gd29ybGQ"))
|
(expect= (base64url "hello world") "aGVsbG8gd29ybGQ")
|
||||||
|
|
|
@ -82,6 +82,7 @@ in {
|
||||||
zyxel-bootconfig = callPackage ./zyxel-bootconfig {};
|
zyxel-bootconfig = callPackage ./zyxel-bootconfig {};
|
||||||
min-collect-garbage = callPackage ./min-collect-garbage {};
|
min-collect-garbage = callPackage ./min-collect-garbage {};
|
||||||
min-copy-closure = callPackage ./min-copy-closure {};
|
min-copy-closure = callPackage ./min-copy-closure {};
|
||||||
|
minisock = callPackage ./minisock {};
|
||||||
nellie = callPackage ./nellie {};
|
nellie = callPackage ./nellie {};
|
||||||
netlink-lua = callPackage ./netlink-lua {};
|
netlink-lua = callPackage ./netlink-lua {};
|
||||||
odhcp-script = callPackage ./odhcp-script {};
|
odhcp-script = callPackage ./odhcp-script {};
|
||||||
|
|
|
@ -31,6 +31,7 @@ in writeScriptBin "fennelrepl" ''
|
||||||
package.cpath = ${lib.strings.escapeShellArg luacpath} .. ";" .. (package.cpath or "")
|
package.cpath = ${lib.strings.escapeShellArg luacpath} .. ";" .. (package.cpath or "")
|
||||||
local fennel = require "fennel"
|
local fennel = require "fennel"
|
||||||
table.insert(package.loaders or package.searchers,1, fennel.searcher)
|
table.insert(package.loaders or package.searchers,1, fennel.searcher)
|
||||||
|
fennel['macro-path'] = "${anoia.dev}/share/lua/${lua.luaversion}/?.fnl;" .. fennel['macro-path']
|
||||||
|
|
||||||
local more_fennel = os.getenv("FENNEL_PATH")
|
local more_fennel = os.getenv("FENNEL_PATH")
|
||||||
if more_fennel then
|
if more_fennel then
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
(local { : view &as fennel } (require :fennel))
|
(local { : view &as fennel } (require :fennel))
|
||||||
(local anoia (require :anoia))
|
(local anoia (require :anoia))
|
||||||
|
(import-macros { : expect= } :anoia.assert)
|
||||||
|
|
||||||
|
;; nix-shell --run "cd pkgs/ifwait && fennelrepl test-ifwait.fnl"
|
||||||
|
|
||||||
(var fake-system (fn [s] (print "executing " s)))
|
(var fake-system (fn [s] (print "executing " s)))
|
||||||
(tset anoia :system #(fake-system $1))
|
(tset anoia :system #(fake-system $1))
|
||||||
|
|
||||||
(macro expect= [actual expected]
|
|
||||||
`(let [ve# (view ,expected)
|
|
||||||
va# (view ,actual)]
|
|
||||||
(when (not (= ve# va#))
|
|
||||||
(assert false
|
|
||||||
(.. "\nexpected " ve# "\ngot " va#)
|
|
||||||
))))
|
|
||||||
|
|
||||||
(fn event-generator [events]
|
(fn event-generator [events]
|
||||||
(coroutine.wrap
|
(coroutine.wrap
|
||||||
|
@ -117,3 +113,5 @@
|
||||||
(set upsies [])
|
(set upsies [])
|
||||||
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
|
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
|
||||||
(expect= upsies [:u :u :u :u]))
|
(expect= upsies [:u :u :u :u]))
|
||||||
|
|
||||||
|
(print "OK")
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
{ lua, lib, fetchFromGitHub }:
|
||||||
|
let pname = "minisock";
|
||||||
|
in lua.pkgs.buildLuaPackage {
|
||||||
|
inherit pname;
|
||||||
|
version = "0.1"; # :shrug:
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
repo = "minisock";
|
||||||
|
owner = "philanc";
|
||||||
|
rev = "a20db2aaa871653c61045019633279167cf1b458";
|
||||||
|
hash = "sha256-zB9KSt0WEGCSYTLA6W9QrsVRFEZYaoBBeXx9VEXmsGY=";
|
||||||
|
};
|
||||||
|
|
||||||
|
makeFlags = [ "LUADIR=." "minisock.so" ];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p "$out/lib/lua/${lua.luaversion}"
|
||||||
|
cp ${pname}.so "$out/lib/lua/${lua.luaversion}/"
|
||||||
|
'';
|
||||||
|
|
||||||
|
}
|
|
@ -1,16 +1,9 @@
|
||||||
(local { : view} (require :fennel))
|
(local { : view} (require :fennel))
|
||||||
|
(import-macros { : expect= } :anoia.assert)
|
||||||
|
|
||||||
(set _G.arg (doto [] (tset 0 "test")))
|
(set _G.arg (doto [] (tset 0 "test")))
|
||||||
(local subject (require :watch))
|
(local subject (require :watch))
|
||||||
|
|
||||||
(macro expect= [actual expected]
|
|
||||||
`(let [ve# (view ,expected)
|
|
||||||
va# (view ,actual)]
|
|
||||||
(when (not (= ve# va#))
|
|
||||||
(assert false
|
|
||||||
(.. "\nexpected " ve# "\ngot " va#)
|
|
||||||
))))
|
|
||||||
|
|
||||||
(let [params
|
(let [params
|
||||||
{:matches {:devname "foo" :partname "my-usbstick"}}]
|
{:matches {:devname "foo" :partname "my-usbstick"}}]
|
||||||
(expect= (subject.event-matches? params {}) false)
|
(expect= (subject.event-matches? params {}) false)
|
||||||
|
|
Loading…
Reference in New Issue