watch.fnl (2431B)
1 (local { : assoc : system : dirname } (require :anoia)) 2 (local { : mktree : rmtree : symlink } (require :anoia.fs)) 3 (local { : AF_LOCAL : SOCK_STREAM } (require :anoia.net.constants)) 4 (local ll (require :lualinux)) 5 6 (local { : view } (require :fennel)) 7 8 (fn parse-args [args] 9 (match args 10 ["-s" service & rest] (assoc (parse-args rest) :service service) 11 ["-n" path & rest] (assoc (parse-args rest) :linkname path) 12 matches { :matches (table.concat matches " ") } 13 _ nil)) 14 15 (fn %% [fmt ...] (string.format fmt ...)) 16 17 (var up :unknown) 18 19 (fn start-service [devname linkname service] 20 (match (if linkname (symlink (.. "/dev/" devname) linkname) true) 21 ok (pcall system (%% "s6-rc-up-tree %q" service)) 22 (nil err) false)) 23 24 (fn stop-service [linkname service] 25 (match (pcall system (%% "s6-rc -b -d change %q" service)) 26 ok (if linkname (os.remove linkname) true) 27 (nil err) false)) 28 29 (fn toggle-service [devname linkname service wanted?] 30 (when (not (= up wanted?)) 31 (set up 32 (if wanted? 33 (start-service devname linkname service) 34 (not (stop-service linkname service)))))) 35 36 (fn parse-uevent [s] 37 (when s 38 (let [(nl nxt) (string.find s "\0" 1 true)] 39 (collect [k v (string.gmatch 40 (string.sub s (+ 1 nxt)) 41 "(%g-)=(%g+)")] 42 (k:lower) v)))) 43 44 (fn run-with-fh [fh args] 45 (set up :unknown) 46 (let [parameters 47 (assert (parse-args args) (.. "can't parse args: " (table.concat args " ")))] 48 (when parameters.linkname (mktree (dirname parameters.linkname))) 49 (var finished? false) 50 51 (print "registering for events" (fh:write parameters.matches)) 52 53 (while (not finished?) 54 (let [e (parse-uevent (fh:read))] 55 (when e 56 (let [wanted? (. {:add true :change true} e.action)] 57 (toggle-service e.devname parameters.linkname parameters.service wanted?))) 58 (set finished? (= e nil)) 59 )))) 60 61 (fn unix-connect [pathname] 62 (let [addr (string.pack "=Hz" AF_LOCAL pathname)] 63 (match (ll.socket AF_LOCAL SOCK_STREAM 0) 64 sock (doto sock (ll.connect addr)) 65 (nil err) (error err)))) 66 67 68 (fn run [args] 69 (let [fd (assert (unix-connect "/run/devout.sock")) 70 devout { 71 :read #(ll.read fd) 72 :write #(ll.write fd $2) 73 :close #(ll.close fd) 74 }] 75 (run-with-fh devout arg))) 76 77 { : run : run-with-fh }