syndev/src/uevent_dump.nim

107 lines
3.4 KiB
Nim

# 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)