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
|
||||
|
||||
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 { : view } (require :fennel))
|
||||
(import-macros { : expect= } :anoia.assert)
|
||||
(local { : merge : dup } (require :anoia))
|
||||
|
||||
;; nix-shell --run "cd modules/dhcp6c && fennelrepl acquire-wan-address-test.fnl"
|
||||
|
||||
(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 []
|
||||
(let [deleted
|
||||
(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 = ./.;
|
||||
nativeBuildInputs = [ fennel ];
|
||||
buildInputs = with lua.pkgs; [ luafilesystem ];
|
||||
buildPhase = ''
|
||||
for f in *.fnl ; do
|
||||
fennel --compile $f > `basename $f .fnl`.lua
|
||||
done
|
||||
'';
|
||||
outputs = [ "out" "dev" ];
|
||||
|
||||
doCheck = true;
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p "$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/
|
||||
(assert (= (hash "Hello") 210676686969))
|
||||
(assert (= (hash "Hello!") 6952330670010))
|
||||
(expect= (hash "Hello") 210676686969)
|
||||
(expect= (hash "Hello!") 6952330670010)
|
||||
|
||||
(assert (= (base64url "hello world") "aGVsbG8gd29ybGQ"))
|
||||
(expect= (base64url "hello world") "aGVsbG8gd29ybGQ")
|
||||
|
|
|
@ -82,6 +82,7 @@ in {
|
|||
zyxel-bootconfig = callPackage ./zyxel-bootconfig {};
|
||||
min-collect-garbage = callPackage ./min-collect-garbage {};
|
||||
min-copy-closure = callPackage ./min-copy-closure {};
|
||||
minisock = callPackage ./minisock {};
|
||||
nellie = callPackage ./nellie {};
|
||||
netlink-lua = callPackage ./netlink-lua {};
|
||||
odhcp-script = callPackage ./odhcp-script {};
|
||||
|
|
|
@ -31,6 +31,7 @@ in writeScriptBin "fennelrepl" ''
|
|||
package.cpath = ${lib.strings.escapeShellArg luacpath} .. ";" .. (package.cpath or "")
|
||||
local fennel = require "fennel"
|
||||
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")
|
||||
if more_fennel then
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
(local { : view &as fennel } (require :fennel))
|
||||
(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)))
|
||||
(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]
|
||||
(coroutine.wrap
|
||||
|
@ -117,3 +113,5 @@
|
|||
(set upsies [])
|
||||
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
|
||||
(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))
|
||||
(import-macros { : expect= } :anoia.assert)
|
||||
|
||||
(set _G.arg (doto [] (tset 0 "test")))
|
||||
(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
|
||||
{:matches {:devname "foo" :partname "my-usbstick"}}]
|
||||
(expect= (subject.event-matches? params {}) false)
|
||||
|
|
Loading…
Reference in New Issue