Add inotify_actor
This commit is contained in:
parent
1e0fa6c2a3
commit
e700953551
|
@ -0,0 +1,3 @@
|
|||
version 1 .
|
||||
|
||||
InotifyMessage = <inotify @path string @event symbol @cookie int @name string> .
|
|
@ -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)
|
|
@ -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))
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "20231005"
|
||||
version = "20231010"
|
||||
author = "Emery Hemingway"
|
||||
description = "Utilites for Syndicated Actors and Synit"
|
||||
license = "unlicense"
|
||||
|
|
Loading…
Reference in New Issue