liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

svc.fnl (3884B)


      1 (local inotify (require :inotify))
      2 (local { : file-exists? : dirname : append-path } (require :anoia))
      3 (local { : file-type : dir : mktree &as fs } (require :anoia.fs))
      4 (local { : readlink } (require :lualinux))
      5 
      6 (fn read-line [name]
      7   (with-open [f (assert (io.open name :r) (.. "can't open file " name))]
      8     (f:read "*l")))
      9 
     10 
     11 ;; If the directory is missing, we cannot add a inotify watch
     12 ;; for it.  We need a watch that opens the parent if the pathname is missing,
     13 ;; and it needs to be the resolved path not the syntactic parent.
     14 
     15 ;; If the directory is not missing, then having a watch on the
     16 ;; parent may result in extra wakeups but should only affect
     17 ;; efficiency not correctness
     18 
     19 ;; Each time the directory may have been added we need to update
     20 ;; watches. If it's been removed, the watch will be removed
     21 ;; automatically. inotify_add_watch when it already exists will modify
     22 ;; instead of making a new one, so we can treat it as idempotent
     23 
     24 
     25 (fn resolve-link [pathname]
     26   (if (= (file-type pathname) :link)
     27       (readlink pathname)
     28       pathname))
     29 
     30 (fn watch-fsevents [directory-name]
     31   (let [handle (inotify.init)
     32         parent-name (dirname (resolve-link directory-name))
     33         refresh (fn []
     34                   (handle:addwatch directory-name
     35                                    inotify.IN_CREATE
     36                                    inotify.IN_MOVE
     37                                    inotify.IN_DELETE
     38                                    inotify.IN_DELETE_SELF
     39                                    inotify.IN_MOVED_FROM
     40                                    inotify.IN_MOVED_TO
     41                                    inotify.IN_CLOSE_WRITE)
     42                   (handle:addwatch parent-name
     43                                    inotify.IN_CREATE
     44                                    inotify.IN_DELETE))]
     45     ;; if you are using poll() to check for events on this
     46     ;; watcher and on other events at the same time, be sure
     47     ;; to call fileno each time around the loop instead
     48     ;; of only once
     49     {
     50      :fileno #(do (refresh) (handle:fileno))
     51      :wait #(do (refresh) (handle:read))
     52      :close #(handle:close)
     53      }))
     54 
     55 (fn write-value [pathname value]
     56   (mktree (dirname pathname))
     57   (match (type value)
     58     "string"
     59     (with-open [f (assert (io.open pathname :w) (.. "can't open " pathname))]
     60       (f:write value))
     61     "table" (each [k v (pairs value)]
     62               (write-value (append-path pathname k) v))))
     63 
     64 (fn read-value [pathname]
     65   (case (file-type pathname)
     66     nil nil
     67     :directory
     68     (collect [f (fs.dir pathname)]
     69       (when (not (or (= f ".") (= f "..")))
     70         (values f (read-value (append-path pathname f)))))
     71     :file
     72     (read-line pathname)
     73     :link
     74     (read-line pathname)
     75     unknown
     76     (error (.. "can't read " pathname " of kind \"" unknown.mode "\""))))
     77 
     78 (fn events [self]
     79   (coroutine.wrap
     80    #(while true
     81       (while (not (self:ready?)) (self:wait))
     82       (coroutine.yield self)
     83       (self:wait))))
     84 
     85 (fn open [directory]
     86   (let [outputs-dir (append-path directory ".outputs")
     87         has-file? #(file-exists? (append-path directory $1))
     88         watcher (watch-fsevents outputs-dir)
     89         properties-dir (append-path directory ".properties")]
     90     {
     91      :ready? (fn [self]
     92                (and (has-file? ".outputs/state")
     93                     (not (has-file? ".outputs/.lock"))))
     94      :property (fn [_ filename]
     95                  (read-value (append-path properties-dir filename)))
     96      :output (fn [_ filename new-value]
     97                (if new-value
     98                    (write-value (append-path outputs-dir filename) new-value)
     99                    (or
    100                     (read-value (append-path outputs-dir filename))
    101                     (read-value (append-path properties-dir filename)))))
    102      :wait #(watcher:wait)
    103      :close #(watcher:close)
    104      :fileno #(watcher:fileno)
    105      : directory
    106      : events
    107      }))
    108 
    109 { : open }