Compare commits

...

4 Commits

Author SHA1 Message Date
Daniel Barlow 032d0f8aca add netlink socket
it's not hooked up to anything yet, but it proves we can
do this with lualinux
2024-04-23 23:34:25 +01:00
Daniel Barlow b8ac9e5279 convert devout from minisock to lualinux 2024-04-23 23:33:11 +01:00
Daniel Barlow ff2604ca5d think 2024-04-23 23:30:50 +01:00
Daniel Barlow 72789984ce add lualinux package 2024-04-23 22:41:38 +01:00
5 changed files with 128 additions and 31 deletions

View File

@ -4676,3 +4676,57 @@ needs to filter, but the client will anyway have to do some message
parsing so they can distinguish add from remove parsing so they can distinguish add from remove
* where do we start? * where do we start?
Sun Apr 21 13:31:48 BST 2024
We have the mechanics of it working (albeit implemented in the
simplest possible terms), we need to glue it to some I/O
1) open a netlink socket and read the events from it
2) "create a PF_UNIX socket of type SOCK_STREAM, and accept connections on it, then each time you accept a connection, you get a new fd"
- accept connection
- read terms from it
- register callback that writes event to connected socket
minisock has no support for "test if fd is ready" or "wait for [fds]
to become ready", either we need poll() or we could add a call for "is
this fd ready to read" and use coroutines. Fork minisock or add as
another library?
[ if we fork minisock we could expose the protocol param to Lua
so we could use it for netlink ]
Tue Apr 23 19:13:45 BST 2024
we could convert from minisock to lualinux. if we can also use that to
get rid of nellie and/or lfs, the size tradeoff is minimal
---
Is there some way we could test the devout event loop?
I can register a fd with a callback
when the fd is ready, my callback is called
when the callback return true it remains registered
when the callback return true it is unregistered and the fd is closed
loop.register
loop.registered?
loop.feed
Tue Apr 23 20:34:03 BST 2024
I think we could make the event loop abstraction leak less?
It's not actually a _loop_, all the actual GOTO 10 happens
outside of it
1) see if we can do netlink in lualinux
2) if so, convert it to lualinux
3) add netlink socket to event loop
4) make it send messages to subscribers
5) package it
6) make inout test use it instead of uevent-watcher
7) write an inout test variant that has the stick inserted
at boot time already

View File

@ -71,6 +71,7 @@ in {
levitate = callPackage ./levitate {}; levitate = callPackage ./levitate {};
libubootenv = callPackage ./libubootenv {}; libubootenv = callPackage ./libubootenv {};
linotify = callPackage ./linotify {}; linotify = callPackage ./linotify {};
lualinux = callPackage ./lualinux {};
# we need to build real lzma instead of using xz, because the lzma # we need to build real lzma instead of using xz, because the lzma
# decoder in u-boot doesn't understand streaming lzma archives # decoder in u-boot doesn't understand streaming lzma archives

View File

@ -6,7 +6,7 @@
, fennel , fennel
, stdenv , stdenv
, fennelrepl , fennelrepl
, minisock , lualinux
}: }:
stdenv.mkDerivation { stdenv.mkDerivation {
name = "devout"; name = "devout";
@ -15,12 +15,12 @@ stdenv.mkDerivation {
installPhase = '' installPhase = ''
mkdir -p $out/bin mkdir -p $out/bin
cp -p ${writeFennel "devout" { cp -p ${writeFennel "devout" {
packages = [fennel anoia nellie lua.pkgs.luafilesystem minisock]; packages = [fennel anoia nellie lua.pkgs.luafilesystem lualinux];
mainFunction = "run"; mainFunction = "run";
} ./devout.fnl} $out/bin/devout } ./devout.fnl} $out/bin/devout
''; '';
checkPhase = '' checkPhase = ''
LUA_CPATH=${minisock}/lib/lua/5.3/?.so\;$LUA_CPATH \ LUA_CPATH=${lualinux}/lib/lua/5.3/?.so\;$LUA_CPATH \
fennelrepl ./test.fnl fennelrepl ./test.fnl
''; '';
doCheck = true; doCheck = true;

View File

@ -1,8 +1,8 @@
(local sock (require :minisock)) (local ll (require :lualinux))
(local { : view } (require :fennel)) (local { : view } (require :fennel))
(fn trace [expr] (fn trace [expr]
(doto expr (print :TRACE (view expr)))) (do (print :TRACE (view expr)) expr))
(fn parse-uevent [s] (fn parse-uevent [s]
(let [at (string.find s "@" 1 true) (let [at (string.find s "@" 1 true)
@ -50,29 +50,39 @@
:unsubscribe (fn [_ id] (tset subscribers id nil)) :unsubscribe (fn [_ id] (tset subscribers id nil))
})) }))
;; #define POLLIN 0x0001 ;; grepped from kernel headers
;; #define POLLPRI 0x0002
;; #define POLLOUT 0x0004 (local POLLIN 0x0001)
;; #define POLLERR 0x0008 (local POLLPRI 0x0002)
;; #define POLLHUP 0x0010 (local POLLOUT 0x0004)
;; #define POLLNVAL 0x0020 (local POLLERR 0x0008)
(local POLLHUP 0x0010)
(local POLLNVAL 0x0020)
(local AF_LOCAL 1)
(local AF_NETLINK 16)
(local SOCK_STREAM 1)
(local SOCK_DGRAM 2)
(local SOCK_RAW 3)
(local NETLINK_KOBJECT_UEVENT 15)
(fn unix-socket [name] (fn unix-socket [name]
(let [addr (.. "\1\0" name "\0\0\0\0\0") (let [addr (.. "\1\0" name "\0\0\0\0\0")]
(sock err) (sock.bind addr)] (match (ll.socket AF_LOCAL SOCK_STREAM 0)
(assert sock err))) fd (match (ll.bind fd addr)
0 (doto fd (ll.listen 32))
(nil err) (values nil err))
(nil err) (values nil err))))
(fn pollfds-for [fds] (fn pollfds-for [fds]
(table.concat (icollect [_ v (ipairs fds)] (string.pack "iHH" v 1 0)))) (icollect [_ v (ipairs fds)]
(bor (lshift v 32) (lshift 1 16))))
(fn unpack-pollfds [pollfds] (fn unpack-pollfds [pollfds]
(var i 1) (collect [_ v (ipairs pollfds)]
(let [fds {}] (let [fd (band (rshift v 32) 0xffffffff)
(while (< i (# pollfds)) revent (band v 0xffff)]
(let [(fd _ revents i_) (string.unpack "iHH" pollfds i)] (values fd revent))))
(if (> revents 0) (tset fds fd revents))
(set i i_)))
fds))
(fn parse-terms [str] (fn parse-terms [str]
(print :terms str) (print :terms str)
@ -80,7 +90,7 @@
(string.match n "(.-)=(.+)"))) (string.match n "(.-)=(.+)")))
(fn handle-client [db client] (fn handle-client [db client]
(match (trace (sock.read client)) (match (ll.read client)
"" (do "" (do
(db:unsubscribe client) (db:unsubscribe client)
false) false)
@ -88,11 +98,18 @@
(db:subscribe (db:subscribe
client client
(fn [e] (fn [e]
(sock.write client (view e))) (ll.write client (view e)))
(parse-terms s)) (parse-terms s))
true) true)
(nil err) (do (print err) false))) (nil err) (do (print err) false)))
(fn open-netlink [groups]
(match (ll.socket AF_NETLINK SOCK_RAW NETLINK_KOBJECT_UEVENT)
fd (doto fd (ll.bind (string.pack "I2I2I4I4" ; family pad pid groups
AF_NETLINK 0 0 groups)))
(nil errno) (values nil errno)))
(fn event-loop [] (fn event-loop []
(let [fds {}] (let [fds {}]
{ {
@ -101,27 +118,30 @@
(each [fd revent (pairs revents)] (each [fd revent (pairs revents)]
(when (not ((. fds fd) fd)) (when (not ((. fds fd) fd))
(tset fds fd nil) (tset fds fd nil)
(sock.close fd)))) (ll.close fd))))
:fds #(icollect [fd _ (pairs fds)] fd) :fds #(icollect [fd _ (pairs fds)] fd)
:_tbl #(do fds) ;exposed for tests :_tbl #(do fds) ;exposed for tests
})) }))
(fn run [] (fn run []
(let [[sockname] arg (let [[sockname nl-groups] arg
s (unix-socket sockname) s (unix-socket sockname)
db (database) db (database)
nl (open-netlink nl-groups)
loop (event-loop)] loop (event-loop)]
(loop:register (loop:register
s s
#(match (sock.accept s) #(match (ll.accept s)
(client addr) (client addr)
(do (do
(loop:register client (partial handle-client db)) (loop:register client (partial handle-client db))
true))) true)))
(loop:register
nl
#(do (print :netlink (ll.read nl)) true))
(while true (while true
(let [pollfds (pollfds-for (loop:fds)) (let [pollfds (pollfds-for (loop:fds))]
(rpollfds numfds) (sock.poll pollfds 1000)] (ll.poll pollfds 5000)
(when (> numfds 0) (loop:feed (unpack-pollfds pollfds))))))
(loop:feed (unpack-pollfds rpollfds)))))))
{ : database : run : event-loop } { : database : run : event-loop }

22
pkgs/lualinux/default.nix Normal file
View File

@ -0,0 +1,22 @@
{ lua, lib, fetchFromGitHub }:
let
pname = "lualinux";
src = fetchFromGitHub {
repo = "lualinux";
owner = "philanc";
rev = "1d4c962aad9cbe01c05df741b91e8b39c356362c";
hash = "sha256-+Ys4sERG+TI8nRzG38UP+KqbH0efspaX0j4IHCt56RI=";
};
in lua.pkgs.buildLuaPackage {
inherit pname;
version = "0.1"; # :shrug:
inherit src;
makeFlags = [ "LUADIR=." "lualinux.so" ];
installPhase = ''
mkdir -p "$out/lib/lua/${lua.luaversion}"
cp ${pname}.so "$out/lib/lua/${lua.luaversion}/"
'';
}