From f51f92c30df1ada9c245c557d6d14202863256cb Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Fri, 15 Mar 2024 09:56:40 +0000 Subject: [PATCH] Rewrite syndev to uevent_dump --- src/Tupfile | 2 +- src/syndev.nim | 98 ---------------------------------------- src/uevent_dump.nim | 106 ++++++++++++++++++++++++++++++++++++++++++++ syndev.nimble | 8 ++-- 4 files changed, 111 insertions(+), 103 deletions(-) delete mode 100644 src/syndev.nim create mode 100644 src/uevent_dump.nim diff --git a/src/Tupfile b/src/Tupfile index 0784bc1..a9ea34f 100644 --- a/src/Tupfile +++ b/src/Tupfile @@ -1,3 +1,3 @@ include_rules -: syndev.nim | $(SYNDICATE_PROTOCOL) |> !nim_bin |> {bin} +: foreach *.nim | $(SYNDICATE_PROTOCOL) |> !nim_bin |> {bin} : {bin} |> !assert_built |> diff --git a/src/syndev.nim b/src/syndev.nim deleted file mode 100644 index 77e71f5..0000000 --- a/src/syndev.nim +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: ☭ Emery Hemingway -# SPDX-License-Identifier: Unlicense - -# *Do not misinterpret any of this as documentation for handling Linux uevents.* - -import std/[asyncdispatch, nativesockets, asyncnet, os, parseutils, posix] -import preserves, syndicate - -var - AF_NETLINK {.importc, header: "sys/socket.h", nodecl.}: uint16 - NETLINK_KOBJECT_UEVENT {.importc, header: "linux/netlink.h", nodecl.}: cint - -type - Sockaddr_nl {.importc: "struct sockaddr_nl", header: "linux/netlink.h".} = object - nl_family: uint16 - nl_pad: uint16 - nl_pid: uint32 - nl_groups: uint32 - -proc initSockaddr(family = AF_NETLINK; pid, groups = 0'u32): Sockaddr_nl = - Sockaddr_nl(nl_family: family, nl_pid: pid, nl_groups: groups) - -proc saddr(sa_nl: var Sockaddr_nl): ptr Sockaddr = - cast[ptr Sockaddr](addr sa_nl) - -proc openUeventSocket: AsyncSocket = - result = newAsyncSocket( - cint AF_NETLINK, - posix.SOCK_DGRAM, - NETLINK_KOBJECT_UEVENT, - buffered = false - ) - var sa = initSockaddr(pid = uint32 getPid(), groups = 1) - if bindAddr(result.getFd, saddr sa, SockLen sizeof(sa)) != 0: - close(result) - raiseOSError(osLastError(), "failed to bind Netlink socket") - if sa.nl_family != AF_NETLINK: - close(result) - raise newException(IOError, "Netlink not supported") - -proc main(facet: Facet; ds: Ref; sock: AsyncSocket) = - var - buf = newString(1 shl 14) - msg = initRecord("uevent", "".toPreserve, "".toPreserve, "".toSymbol, initDictionary(), 0.toPreserve) - key, val: string - proc recvUevent {.gcsafe.} = - let fut = recvInto(sock, buf[0].addr, buf.len) - addCallback(fut, facet) do (turn: var Turn): - let n = read fut - if n < 1: - close(sock) - else: - var i = 0 - while i < n: - inc i, skipWhile(buf, {'\0'}, i) - inc i, parseUntil(buf, key, {'=', '@'}, i) - if i < n: - let sep = buf[i] - inc i, parseUntil(buf, val, '\0', i+1)+1 - case sep - of '@': - if msg.record[3].dict.len > 0: - cannonicalize(msg.record[3]) - message(turn, ds, msg) - msg.record[3].dict.setLen(0) - of '=': - if key == "SUBSYSTEM": - msg.record[0].string = val - elif key == "DEVPATH": - msg.record[1].string = val - elif key == "ACTION": - msg.record[2].symbol = Symbol val - elif key == "SEQNUM": - discard parseBiggestInt(val, msg.record[4].int) - else: - # TODO: check if val can be an integer - var num: BiggestInt - if parseBiggestInt(val, num) == val.len: - add(msg.record[3].dict, (key.toSymbol, num.toPreserve,)) - else: - add(msg.record[3].dict, (key.toSymbol, val.toPreserve,)) - val.setLen(0) - else: - stderr.writeLine "uevent parser synchronization lost" - break - recvUevent() - recvUevent() - -type Args {.preservesDictionary.} = object - machine: Ref - -runActor("syndev") do (root: Ref; turn: var Turn): - connectStdio(root, turn) - during(turn, root, ?Args) do (machine: Ref): - let sock = openUeventSocket() - main(turn.facet, machine, sock) - do: - close(sock) diff --git a/src/uevent_dump.nim b/src/uevent_dump.nim new file mode 100644 index 0000000..50b54d0 --- /dev/null +++ b/src/uevent_dump.nim @@ -0,0 +1,106 @@ +# SPDX-FileCopyrightText: ☭ Emery Hemingway +# SPDX-License-Identifier: Unlicense + +# *Do not misinterpret any of this as documentation for handling Linux uevents.* + +import std/[nativesockets, os, parseutils, posix] +import pkg/sys/[handles, ioqueue, sockets] +import preserves, syndicate, syndicate/relays + +var + AF_NETLINK {.importc, header: "sys/socket.h", nodecl.}: uint16 + NETLINK_KOBJECT_UEVENT {.importc, header: "linux/netlink.h", nodecl.}: cint + +type + Sockaddr_nl {.importc: "struct sockaddr_nl", header: "linux/netlink.h".} = object + nl_family: uint16 + nl_pad: uint16 + nl_pid: uint32 + nl_groups: uint32 + +proc initSockaddr(family = AF_NETLINK; pid, groups: uint32): Sockaddr_nl = + Sockaddr_nl(nl_family: family, nl_pid: pid, nl_groups: groups) + +proc saddr(sa_nl: var Sockaddr_nl): ptr Sockaddr = + cast[ptr Sockaddr](addr sa_nl) + +proc openUeventSocket: SocketHandle = + result = createNativeSocket( + cint AF_NETLINK, + cint posix.SOCK_DGRAM, + NETLINK_KOBJECT_UEVENT, + ) + var sa = initSockaddr(pid = uint32 getPid(), groups = 1) + if bindAddr(result, saddr sa, SockLen sizeof(sa)) != 0: + close(result) + raiseOSError(osLastError(), "failed to bind Netlink socket") + if sa.nl_family != AF_NETLINK: + close(result) + raise newException(IOError, "Netlink not supported") + +proc openUeventSocketAsync: AsyncConn[sockets.Protocol.Unix] = + let fd = openUeventSocket() + var flags = fcntl(fd.cint, F_GETFL) + if flags < 0: + raiseOSError(osLastError()) + flags = flags or O_NONBLOCK + if fcntl(fd.cint, F_SETFL, flags) < 0: + raiseOSError(osLastError()) + AsyncConn[sockets.Protocol.Unix] fd.SocketFD.initHandle.newAsyncSocket + +proc sendMessage(turn: var Turn; ds: Cap; buf: string; n: int) = + var + msg = initRecord("uevent", toPreserves"", toPreserves"", toSymbol"", initDictionary(), 0.toPreserves) + key, val: string + i = 0 + while i < n: + inc i, skipWhile(buf, {'\0'}, i) + inc i, parseUntil(buf, key, {'=', '@'}, i) + if i < n: + let sep = buf[i] + inc i, parseUntil(buf, val, '\0', i+1)+1 + case sep + of '@': discard + of '=': + if key == "SUBSYSTEM": + msg.record[0].string = val + elif key == "DEVPATH": + msg.record[1].string = val + elif key == "ACTION": + msg.record[2].symbol = Symbol val + elif key == "SEQNUM": + msg.record[4] = parsePreserves(val) + else: + # TODO: check if val can be an integer + var num: BiggestInt + if parseBiggestInt(val, num) == val.len: + add(msg.record[3].dict, (key.toSymbol, num.toPreserve,)) + else: + add(msg.record[3].dict, (key.toSymbol, val.toPreserve,)) + val.setLen(0) + else: + stderr.writeLine "uevent parser failure" + return + if msg.record[3].dict.len > 0: + cannonicalize(msg.record[3]) + message(turn, ds, msg) + msg.record[3].dict.setLen(0) + +proc loop(facet: Facet; ds: Cap; sock: AsyncConn[sockets.Protocol.Unix]) {.asyncio.} = + let buf = new string + buf[].setLen(16 shl 10) + while true: + let n = read(sock, buf) + if n < 1: stopActor(facet) + else: + proc act(turn: var Turn) = + sendMessage(turn, ds, buf[], n) + run(facet, act) + +runActor("uevent_dump") do (turn: var Turn): + let sock = openUeventSocketAsync() + onStop(turn) do (turn: var Turn): + close(sock) + resolveEnvironment(turn) do (turn: var Turn; ds: Cap): + discard trampoline: + whelp loop(turn.facet, ds, sock) diff --git a/syndev.nimble b/syndev.nimble index 5d43f3d..92a57e2 100644 --- a/syndev.nimble +++ b/syndev.nimble @@ -1,13 +1,13 @@ # Package -version = "20230704" +version = "20240315" author = "Emery Hemingway" -description = "Syndicated actor for publishing Linux device events" +description = "Syndicate utilties for Linux." license = "Unlicense" srcDir = "src" -bin = @["syndev"] +bin = @["uevent_dump"] # Dependencies -requires "nim >= 1.6.12", "syndicate >= 20230701" +requires "nim >= 1.6.12", "syndicate >= 20240208", "https://github.com/ehmry/nim-sys.git#b974e1a4ca6ae7d89fc9e7b3714b1e7daf6f33e5", "https://github.com/nim-works/cps"