99 lines
3.2 KiB
Nim
99 lines
3.2 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", "".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)
|