diff --git a/pkgs/s6-rc-up-tree/default.nix b/pkgs/s6-rc-up-tree/default.nix index b6d0576..529193a 100644 --- a/pkgs/s6-rc-up-tree/default.nix +++ b/pkgs/s6-rc-up-tree/default.nix @@ -1,4 +1,54 @@ { - writeAshScriptBin + lualinux, + writeFennel, + anoia, + fennel, + stdenv, + fennelrepl, }: -writeAshScriptBin "s6-rc-up-tree" {} (builtins.readFile ./s6-rc-up-tree.sh) +stdenv.mkDerivation { + name = "s6-rc-up-tree"; + src = ./.; + nativeBuildInputs = [ fennelrepl ]; + # propagatedBuildInputs = [ s6-rc-up-tree ]; + installPhase = '' + mkdir -p $out/bin + cp -p ${writeFennel "s6-rc-up-tree" { + packages = [fennel + # anoia nellie + lualinux ] ; + mainFunction = "run"; + } ./s6-rc-up-tree.fnl } $out/bin/s6-rc-up-tree + ''; + postBuild = '' + export PATH=./scripts:$PATH + patchShebangs ./scripts + export TEST_LOG=./log + fail(){ cat $TEST_LOG | od -c; exit 1; } + expect(){ + test "$(echo $(cat $TEST_LOG))" = "$@" || fail; + } + # given a service with no rdepends, starts only that service + fennelrepl ./test.fnl ${./test-services} turmeric + expect "turmeric" + + # given a controlled service with no rdepends, starts only that service + fennelrepl ./test.fnl ${./test-services} wombat + expect "wombat" + + # uncontrolled rdepends start + fennelrepl ./test.fnl ${./test-services} thyme + expect "thyme rosemary" + + # stopped controlled rdepends don't start + fennelrepl ./test.fnl ${./test-services} enables-wan + expect "enables-wan" # not wattle, even though it depends + + # started controlled rdepends are running, so starting them is harmless + + # descendants which depend on a _different_ controlled service, which is down, don't start + + # descendants which depend on a _different_ controlled service, which is up, do start + + ''; +} diff --git a/pkgs/s6-rc-up-tree/s6-rc-db b/pkgs/s6-rc-up-tree/s6-rc-db new file mode 100644 index 0000000..41f1f30 --- /dev/null +++ b/pkgs/s6-rc-up-tree/s6-rc-db @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +cat << DEPS +one +two +three +four +DEPS diff --git a/pkgs/s6-rc-up-tree/s6-rc-up-tree.fnl b/pkgs/s6-rc-up-tree/s6-rc-up-tree.fnl new file mode 100644 index 0000000..0e0f81f --- /dev/null +++ b/pkgs/s6-rc-up-tree/s6-rc-up-tree.fnl @@ -0,0 +1,58 @@ +(local { : opendir : readdir } (require :lualinux)) +(local { : view } (require :fennel)) + +(fn fail [err] + (print "ERROR" err) + (os.exit 1)) + +(macro with-popen [[handle command] & body] + `(let [,handle (assert (io.popen ,command)) + val# (do ,(unpack body))] + (case (: ,handle :close) + ok# val# + (nil :exit code#) (fail (.. ,command " exited " code#)) + (nil :signal sig#) (fail (.. ,command " killed by " sig#))))) + +(fn popen [command] + (with-popen [fh command] (icollect [v (fh:lines)] v))) + +(fn controlled-services [dir] + (case (opendir dir) ;; FIXME [nit] doesn't closedir + d (collect [filename #(readdir d)] + (if (not (string.match filename "^%.")) + (values filename filename))) + (nil err) (fail (.. "can't open " dir " :" err)))) + +(fn stopped-services [] + (popen (.. "s6-rc -da list"))) + +(fn stopped-controlled-services [dir] + (let [controlled (controlled-services dir)] + (with-popen [h (.. "s6-rc -da list")] + (collect [s (h:lines)] + (if (. controlled s) (values s s)))))) + +(fn dependencies [service] + (popen (.. "s6-rc-db all-dependencies " service))) + +(fn reverse-dependencies [service] + (popen (.. "s6-rc-db -d all-dependencies " service))) + +(fn start-service [name] + (case (os.execute (.. "s6-rc -u change " name)) + (ok) nil + (nil err) (fail err))) + +(fn run [dir] + (let [service (. arg 1) + blocks (stopped-controlled-services (or dir "/run/services/controlled"))] + (print :service service :blocks (view blocks)) + (each [_ s (ipairs (reverse-dependencies service))] + (print :dep s) + (when + (accumulate [start true + _ dep (ipairs (dependencies s))] + (and start (or (= s service) (not (. blocks dep))))) + (start-service s))))) + +{ : run } diff --git a/pkgs/s6-rc-up-tree/scripts/s6-rc b/pkgs/s6-rc-up-tree/scripts/s6-rc new file mode 100755 index 0000000..32145b8 --- /dev/null +++ b/pkgs/s6-rc-up-tree/scripts/s6-rc @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +echo s6-rc $@ +if [ "$1" = "-da" ]; then + if [ "$2" = "list" ]; then + echo wattle # controlled + echo wombat # controlled + echo turmeric # uncontrolled + fi +fi +if [ "$1" = "-u" ]; then + if [ "$2" = "change" ]; then + echo "$3" >> $TEST_LOG + fi +fi diff --git a/pkgs/s6-rc-up-tree/scripts/s6-rc-db b/pkgs/s6-rc-up-tree/scripts/s6-rc-db new file mode 100755 index 0000000..2b8a1c1 --- /dev/null +++ b/pkgs/s6-rc-up-tree/scripts/s6-rc-db @@ -0,0 +1,31 @@ +#!/usr/bin/env sh + +reverse_deps(){ + echo $1 + case "$1" in + thyme) + echo rosemary + ;; + enables-wan) + echo wattle # controlled + ;; + esac +} + +deps(){ + echo $1 + case "$1" in + rosemary) + echo thyme;; + *) + ;; + esac +} + +if test "$1" = "-d" && test "$2" = "all-dependencies"; then + shift; shift; + reverse_deps $@ +elif test "$1" = "all-dependencies"; then + shift; + deps $@ +fi diff --git a/pkgs/s6-rc-up-tree/test-services/daub b/pkgs/s6-rc-up-tree/test-services/daub new file mode 100644 index 0000000..e69de29 diff --git a/pkgs/s6-rc-up-tree/test-services/wan-proxy b/pkgs/s6-rc-up-tree/test-services/wan-proxy new file mode 100644 index 0000000..e69de29 diff --git a/pkgs/s6-rc-up-tree/test-services/wattle b/pkgs/s6-rc-up-tree/test-services/wattle new file mode 100644 index 0000000..e69de29 diff --git a/pkgs/s6-rc-up-tree/test-services/wombat b/pkgs/s6-rc-up-tree/test-services/wombat new file mode 100644 index 0000000..e69de29 diff --git a/pkgs/s6-rc-up-tree/test.fnl b/pkgs/s6-rc-up-tree/test.fnl new file mode 100644 index 0000000..f5c336b --- /dev/null +++ b/pkgs/s6-rc-up-tree/test.fnl @@ -0,0 +1,14 @@ +(local up-tree (require "s6-rc-up-tree")) + +(os.remove (os.getenv "TEST_LOG")) + +(let [[dir & services] arg] + (set arg services) + (up-tree.run dir)) + +;; the service starts +;; the service starts even if it is controlled +;; uncontrolled descendants start +;; controlled descendants don't start +;; descendants which depend on a _different_ controlled service, which is down, don't start +;; descendants which depend on a _different_ controlled service, which is up, do start