#!/bin/sh set +e export PATH="/usr/bin:/bin" error() { echo "$@" >&2 } alt_ab() { if [ "$1" == "a" ]; then echo "b"; else echo "a"; fi } : ${S6_PATH:="${XDG_DATA_HOME:="/etc"}/s6"} : ${S6_DIST_PATH:="${XDG_CONFIG_HOME:="/usr/share"}/nnd/s6/dist"} : ${S6_LIVE_PATH:="${XDG_RUNTIME_DIR:="/run"}/s6-rc"} : ${S6_SCANDIR_PATH:="${XDG_RUNTIME_DIR:="/run"}/service"} : ${S6_RC_PATH:="$S6_PATH/rc"} : ${S6_ENV_PATH:="$S6_PATH/env"} : ${S6_SV_PATH:="$S6_PATH/sv"} # A/B mode, always keep last copy DB_FRESH_ACT() { readlink "$S6_SV_PATH/current" | cut -d. -f2 # if it fails (missing), it will be empty and 'a' will be used as inactive by default } DB_FRESH_NAC() { alt_ab "$(DB_FRESH_ACT)" } generate() { if ! mkdir -p "$S6_SV_PATH"; then error "Failed to create sv directory" return "$?" fi local NAC="$(DB_FRESH_NAC)" if [ -d "$S6_SV_PATH/current.$NAC" ]; then if ! rm -rf "$S6_SV_PATH/current.$NAC"; then error "Failed to remove inactive database path" return "$?" fi fi if ! s6-rc-compile "$S6_SV_PATH/current.$NAC" "$S6_RC_PATH"; then error "Failed to compile current s6 database" return "$?" fi } swap() { local new="current.$(DB_FRESH_NAC)" if ! [ -d "$S6_SV_PATH/$new" ]; then error "There's no database to switch to" return 1 fi if ! s6-rc-db -c "$S6_SV_PATH/$new" check; then error "Database $new is invalid" return "$?" fi if ! ln -sfn "$new" "$S6_SV_PATH/current"; then error "Failed to update A/B current symlink" return "$?" fi } update() { if ! s6-rc-update -l "$S6_LIVE_PATH" "$S6_SV_PATH/current.$(DB_FRESH_ACT)"; then error "Failed to update live state of the database" return "$?" fi } dist() { #for src in "$S6_DIST_PATH/rc"/* "$S6_DIST_PATH/env"/*; do for src in "$S6_DIST_PATH/rc"/*; do local svc="${src#$S6_DIST_PATH/}" if [ ! -e "$S6_PATH/$svc" ]; then default "$svc" || return "$?" fi done } cleanup() { # Remove any dangling invalid symlinks for i in rc env; do removedangle /etc/s6/"$i" || return "$?" done } default() { for svc in $@; do local src="$S6_DIST_PATH/$svc" local target="$S6_PATH/$svc" if ! [ -e "$src" ]; then error "$svc doesn't exist" return "$?" fi if [ -h "$target" ]; then error "$svc is already a default instance" return "$?" fi defaultfunc || return "$?" done } custom() { for svc in $@; do local src="$S6_DIST_PATH/$svc" local target="$S6_PATH/$svc" if ! [ -e "$src" ]; then error "$svc doesn't exist" return "$?" fi if ! [ -h "$target" ]; then error "$svc is already a custom instance" return "$?" fi customfunc || return "$?" done } customfunc() { : ${src:?"Missing src in customfunc"} : ${target:?"Missing target in customfunc"} if ! rm "$target"; then error "Failed to remove distributed directory $target" return "$?" fi if ! cp -r "$src" "$target"; then error "Failed to initialise custom directory $target" return "$?" fi } defaultfunc() { : ${src:?"Missing src in defaultfunc"} : ${target:?"Missing target in defaultfunc"} if ! rm -rf "$target"; then error "Failed to remove custom directory $target" return "$?" fi if ! ln -sv "$src" "$target"; then error "Failed to create reference to $src" return "$?" fi } removedangle() { local dir="$1" find -L "$dir" -maxdepth 1 -type l -exec rm -v -- {} + } # TODO # makeusertree() { # USR="$1" # UID="$(id -u "$USR")" # RUNTIME_DIR="/run/user/$UID" # : ${PARENT_RCDIR:+"/etc/s6/rc"} # s6-usertree-maker \ # -d "$RUNTIME_DIR"/service \ # -r usertree."$UID"/logger.usertree."$UID" \ # -l "$USR" \ # "$USR" \ # "$RUNTIME_DIR"/uncaught-logs/ \ # "$PARENT_RCDIR" # } cmd="$1" shift if [ -z "$cmd" ]; then generate || return "$?" swap || return "$?" update || return "$?" else case "$cmd" in generate|swap|update|dist|custom|default|cleanup) eval "$cmd" $@ || return "$?";; *) error "Invalid command $cmd"; return 1;; esac fi