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