syndev/src/syndev.nim

87 lines
2.8 KiB
Nim

# 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", 0.toPreserve, "".toSymbol, initDictionary())
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:
if buf[i] == '@':
if msg.record[2].dict.len > 0:
cannonicalize(msg.record[1])
message(turn, ds, msg)
msg.record[2].dict.setLen(0)
inc i, parseUntil(buf, val, '\0', i+1)+1
elif key == "SEQNUM":
var seqnum: int
inc i, parseInt(buf, seqnum, i+1)+1
msg.record[0].int = seqnum.BiggestInt
else:
inc i, parseUntil(buf, val, '\0', i+1)+1
if key == "ACTION":
msg.record[1].symbol = Symbol val
else:
add(msg.record[2].dict, (key.toSymbol, val.toPreserve,))
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)