Add inotify_actor

This commit is contained in:
Emery Hemingway 2023-10-10 19:37:30 +01:00
parent 1e0fa6c2a3
commit e700953551
4 changed files with 137 additions and 1 deletions

3
inotify_actor.prs Normal file
View File

@ -0,0 +1,3 @@
version 1 .
InotifyMessage = <inotify @path string @event symbol @cookie int @name string> .

117
src/inotify_actor.nim Normal file
View File

@ -0,0 +1,117 @@
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
## An actor for filesystem monitoring.
import std/[asyncdispatch, asyncfile, os, sets, strutils, tables]
import posix, posix/inotify
import preserves
import syndicate, syndicate/[bags, relays]
from syndicate/protocols/dataspace import Observe
import ./schema/inotify_actor
var IN_NONBLOCK {.importc, nodecl.}: cint
type
Observe = dataspace.Observe[Cap]
# Registry = TableRef[cint, TableRef[Cap, HashSet[string]]]
BootArgs {.preservesDictionary.} = object
dataspace: Cap
proc toMask(sym: Symbol): uint32 =
case sym.string
of "IN_ACCESS": IN_ACCESS
of "IN_MODIFY": IN_MODIFY
of "IN_ATTRIB": IN_ATTRIB
of "IN_CLOSE_WRITE": IN_CLOSE_WRITE
of "IN_CLOSE_NOWRITE": IN_CLOSE_NOWRITE
of "IN_CLOSE": IN_CLOSE
of "IN_OPEN": IN_OPEN
of "IN_MOVED_FROM": IN_MOVED_FROM
of "IN_MOVED_TO": IN_MOVED_TO
of "IN_MOVE": IN_MOVE
of "IN_CREATE": IN_CREATE
of "IN_DELETE": IN_DELETE
of "IN_DELETE_SELF": IN_DELETE_SELF
of "IN_MOVE_SELF": IN_MOVE_SELF
else: 0
func contains(event, bit: uint32): bool = (event and bit) != 0
iterator symbols(event: uint32): Symbol =
if event.contains IN_ACCESS:
yield Symbol"IN_ACCESS"
if event.contains IN_MODIFY:
yield Symbol"IN_MODIFY"
if event.contains IN_ATTRIB:
yield Symbol"IN_ATTRIB"
if event.contains IN_CLOSE_WRITE:
yield Symbol"IN_CLOSE_WRITE"
if event.contains IN_CLOSE_NOWRITE:
yield Symbol"IN_CLOSE_NOWRITE"
if event.contains IN_OPEN:
yield Symbol"IN_OPEN"
if event.contains IN_MOVED_FROM:
yield Symbol"IN_MOVED_FROM"
if event.contains IN_MOVED_TO:
yield Symbol"IN_MOVED_TO"
if event.contains IN_CREATE:
yield Symbol"IN_CREATE"
if event.contains IN_DELETE:
yield Symbol"IN_DELETE"
if event.contains IN_DELETE_SELF:
yield Symbol"IN_DELETE_SELF"
if event.contains IN_MOVE_SELF:
yield Symbol"IN_MOVE_SELF"
if event.contains (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE):
yield Symbol"IN_CLOSE"
if event.contains (IN_MOVED_FROM or IN_MOVED_TO):
yield Symbol"IN_MOVE"
runActor("inotify_actor") do (root: Cap; turn: var Turn):
let buf = newSeq[byte](8192)
let eventPattern = ?Observe(pattern: !InotifyMessage) ?? { 0: grabLit(), 1: grabLit() }
connectStdio(root, turn)
during(turn, root, ?BootArgs) do (ds: Cap):
let inf = inotify_init1(IN_NONBLOCK)
doAssert inf != -1, $inf & " - " & $strerror(errno)
var
registry = initTable[cint, string]()
watchBag: Bag[cint]
let
anf = newAsyncFile(AsyncFD inf)
facet = turn.facet
var fut: Future[int]
proc readEvents() {.gcsafe.} =
fut = readBuffer(anf, buf[0].addr, buf.len)
addCallback(fut, facet) do (turn: var Turn):
let n = read(fut)
doAssert n > 0
for event in inotify_events(buf[0].addr, n):
var msg = InotifyMessage(path: registry[event.wd], cookie: event.cookie.BiggestInt)
if event.len > 0:
let n = event.len
msg.name.setLen(n)
copyMem(msg.name[0].addr, event.name.addr, n)
for i, c in msg.name:
if c == '\0':
msg.name.setLen(i)
break
for sym in event.mask.symbols:
msg.event = sym
message(turn, ds, msg)
readEvents()
readEvents()
during(turn, ds, eventPattern) do (path: string, kind: Symbol):
let wd = inotify_add_watch(inf, path, kind.toMask or IN_MASK_ADD)
doAssert wd > 0, $strerror(errno)
registry[wd] = path
discard watchBag.change(wd, 1)
do:
if watchBag.change(wd, -1, clamp = true) == cdPresentToAbsent:
discard close(wd)
registry.del(wd)
do:
close(anf)

View File

@ -0,0 +1,16 @@
import
preserves
type
InotifyMessage* {.preservesRecord: "inotify".} = object
`path`*: string
`event`*: Symbol
`cookie`*: BiggestInt
`name`*: string
proc `$`*(x: InotifyMessage): string =
`$`(toPreserve(x))
proc encode*(x: InotifyMessage): seq[byte] =
encode(toPreserve(x))

View File

@ -1,6 +1,6 @@
# Package
version = "20231005"
version = "20231010"
author = "Emery Hemingway"
description = "Utilites for Syndicated Actors and Synit"
license = "unlicense"