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