235 lines
4.8 KiB
Bash
Executable File
235 lines
4.8 KiB
Bash
Executable File
#!/bin/sh
|
|
set +e
|
|
|
|
export PATH="/usr/bin:/bin"
|
|
|
|
error() {
|
|
echo "$@" >&2
|
|
}
|
|
|
|
alt_ab() {
|
|
if [ "$1" == "a" ]; then echo "b"; else echo "a"; fi
|
|
}
|
|
|
|
: ${XDG_CONFIG_HOME:="/etc"}
|
|
: ${XDG_DATA_HOME:="/usr/share"}
|
|
: ${XDG_RUNTIME_DIR:="/run"}
|
|
|
|
: ${S6_PATH:="${XDG_CONFIG_HOME}/s6"}
|
|
: ${S6_DIST_PATH:="${XDG_DATA_HOME}/nnd/s6/dist"}
|
|
: ${S6_LIVE_PATH:="${XDG_RUNTIME_DIR}/s6-rc"}
|
|
: ${S6_SCANDIR_PATH:="${XDG_RUNTIME_DIR}/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() {
|
|
# Safety net to make sure we don't overwrite the currently used database
|
|
# If not present, it allows one to switch database as default without updating current database
|
|
# and then rebuilding and overwriting actively used database, resulting in a corrupt state
|
|
if [ -L "$S6_LIVE_PATH/compiled" ]; then
|
|
basename "$(readlink "$S6_LIVE_PATH/compiled")" | cut -d. -f2
|
|
else
|
|
basename "$(readlink "$S6_SV_PATH/current")" | cut -d. -f2
|
|
fi
|
|
# 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_NAC)"; 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
|
|
}
|
|
|
|
disable() {
|
|
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 [ -f "$target" ]; then
|
|
error "$svc is already disabled"
|
|
return "$?"
|
|
fi
|
|
|
|
disablefunc || 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
|
|
}
|
|
|
|
disablefunc() {
|
|
: ${src:?"Missing src in disablefunc"}
|
|
: ${target:?"Missing target in disablefunc"}
|
|
if ! rm "$target"; then
|
|
error "Failed to remove directory $target"
|
|
return "$?"
|
|
fi
|
|
|
|
if ! touch "$target"; then
|
|
error "Failed to create empty file $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|disable|default|cleanup) eval "$cmd" $@ || return "$?";;
|
|
*) error "Invalid command $cmd"; return 1;;
|
|
esac
|
|
fi
|