From e700953551d5a318c2e40fe29ea10a7b383bda85 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Tue, 10 Oct 2023 19:37:30 +0100 Subject: [PATCH] Add inotify_actor --- inotify_actor.prs | 3 + src/inotify_actor.nim | 117 +++++++++++++++++++++++++++++++++++ src/schema/inotify_actor.nim | 16 +++++ syndicate_utils.nimble | 2 +- 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 inotify_actor.prs create mode 100644 src/inotify_actor.nim create mode 100644 src/schema/inotify_actor.nim diff --git a/inotify_actor.prs b/inotify_actor.prs new file mode 100644 index 0000000..b53d093 --- /dev/null +++ b/inotify_actor.prs @@ -0,0 +1,3 @@ +version 1 . + +InotifyMessage = . diff --git a/src/inotify_actor.nim b/src/inotify_actor.nim new file mode 100644 index 0000000..3604b08 --- /dev/null +++ b/src/inotify_actor.nim @@ -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) diff --git a/src/schema/inotify_actor.nim b/src/schema/inotify_actor.nim new file mode 100644 index 0000000..689eb31 --- /dev/null +++ b/src/schema/inotify_actor.nim @@ -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)) diff --git a/syndicate_utils.nimble b/syndicate_utils.nimble index 57dfb16..15c36a6 100644 --- a/syndicate_utils.nimble +++ b/syndicate_utils.nimble @@ -1,6 +1,6 @@ # Package -version = "20231005" +version = "20231010" author = "Emery Hemingway" description = "Utilites for Syndicated Actors and Synit" license = "unlicense"