commit 05991225de57c2441a434142c4a35bc6eddd12fa
parent 7ce1c6bb7d2fb61ab81dfa408310fa2711e908c4
Author: Daniel Barlow <dan@telent.net>
Date: Tue, 25 Mar 2025 23:37:58 +0000
anoia.svc allow open of a service that is not yet running
we change the inotify watcher so that it attempts to monitor
/run/service as well as /run/service/foo. If foo doesn't yet exist
then that call to addwatch fails, so we need to be looking at the
parent if we are to be told when foo gets created
Diffstat:
1 file changed, 47 insertions(+), 13 deletions(-)
diff --git a/pkgs/anoia/svc.fnl b/pkgs/anoia/svc.fnl
@@ -1,22 +1,56 @@
(local inotify (require :inotify))
(local { : file-exists? : dirname : append-path } (require :anoia))
(local { : file-type : dir : mktree &as fs } (require :anoia.fs))
+(local { : readlink } (require :lualinux))
(fn read-line [name]
(with-open [f (assert (io.open name :r) (.. "can't open file " name))]
(f:read "*l")))
+
+;; If the directory is missing, we cannot add a inotify watch
+;; for it. We need a watch that opens the parent if the pathname is missing,
+;; and it needs to be the resolved path not the syntactic parent.
+
+;; If the directory is not missing, then having a watch on the
+;; parent may result in extra wakeups but should only affect
+;; efficiency not correctness
+
+;; Each time the directory may have been added we need to update
+;; watches. If it's been removed, the watch will be removed
+;; automatically. inotify_add_watch when it already exists will modify
+;; instead of making a new one, so we can treat it as idempotent
+
+
+(fn resolve-link [pathname]
+ (if (= (file-type pathname) :link)
+ (readlink pathname)
+ pathname))
+
(fn watch-fsevents [directory-name]
- (let [handle (inotify.init)]
- (handle:addwatch directory-name
- inotify.IN_CREATE
- inotify.IN_MOVE
- inotify.IN_DELETE
- inotify.IN_DELETE_SELF
- inotify.IN_MOVED_FROM
- inotify.IN_MOVED_TO
- inotify.IN_CLOSE_WRITE)
- handle))
+ (let [handle (inotify.init)
+ parent-name (dirname (resolve-link directory-name))
+ refresh (fn []
+ (handle:addwatch directory-name
+ inotify.IN_CREATE
+ inotify.IN_MOVE
+ inotify.IN_DELETE
+ inotify.IN_DELETE_SELF
+ inotify.IN_MOVED_FROM
+ inotify.IN_MOVED_TO
+ inotify.IN_CLOSE_WRITE)
+ (handle:addwatch parent-name
+ inotify.IN_CREATE
+ inotify.IN_DELETE))]
+ ;; if you are using poll() to check for events on this
+ ;; watcher and on other events at the same time, be sure
+ ;; to call fileno each time around the loop instead
+ ;; of only once
+ {
+ :fileno #(do (refresh) (handle:fileno))
+ :wait #(do (refresh) (handle:read))
+ :close #(handle:close)
+ }))
(fn write-value [pathname value]
(mktree (dirname pathname))
@@ -49,12 +83,11 @@
(self:wait))))
(fn open [directory]
- (let [watcher (watch-fsevents directory)
+ (let [outputs-dir (append-path directory ".outputs")
has-file? #(file-exists? (append-path directory $1))
- outputs-dir (append-path directory ".outputs")
+ watcher (watch-fsevents outputs-dir)
properties-dir (append-path directory ".properties")]
{
- :wait #(watcher:read)
:ready? (fn [self]
(and (has-file? ".outputs/state")
(not (has-file? ".outputs/.lock"))))
@@ -66,6 +99,7 @@
(or
(read-value (append-path outputs-dir filename))
(read-value (append-path properties-dir filename)))))
+ :wait #(watcher:wait)
:close #(watcher:close)
:fileno #(watcher:fileno)
: events