init
Signed-off-by: fbt <fbt@fleshless.org>
This commit is contained in:
commit
2b72e15884
264
sm
Executable file
264
sm
Executable file
|
@ -0,0 +1,264 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
|
# Utility functions
|
||||||
|
## Make setting default values a bit less awkward
|
||||||
|
default() {
|
||||||
|
declare -n _p=$1
|
||||||
|
|
||||||
|
if ! [[ "$_p" ]]; then
|
||||||
|
_p=$2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
## Die. Why not?
|
||||||
|
die() {
|
||||||
|
declare code=${1:-0}
|
||||||
|
|
||||||
|
[[ "$2" ]] && printf '%s\n' "$2"
|
||||||
|
exit "$code"
|
||||||
|
}
|
||||||
|
|
||||||
|
## Run the command and wait for it to die
|
||||||
|
svc() {
|
||||||
|
svc::cleanup() {
|
||||||
|
kill -n "$service_stop_signal" "$service_pid"
|
||||||
|
|
||||||
|
pid_wait "$service_pid" && {
|
||||||
|
rm -f $svc_pidfile
|
||||||
|
}
|
||||||
|
}; trap 'svc::cleanup' TERM INT
|
||||||
|
|
||||||
|
svc::reload() {
|
||||||
|
kill -n "$service_reload_signal" "$service_pid"
|
||||||
|
}; trap 'svc::reload' HUP
|
||||||
|
|
||||||
|
exec "$@" &
|
||||||
|
service_pid=$!
|
||||||
|
wait
|
||||||
|
}
|
||||||
|
|
||||||
|
## Respawn implementation
|
||||||
|
respawn() {
|
||||||
|
declare jpid
|
||||||
|
|
||||||
|
respawn::cleanup() {
|
||||||
|
kill -n 15 0
|
||||||
|
wait
|
||||||
|
exit 0
|
||||||
|
}; trap 'svc::reload' TERM
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
"$@" & jpid=$!
|
||||||
|
wait
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
## Run a command with its output discarded
|
||||||
|
nullexec() {
|
||||||
|
"$@" &>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
## Wait for a pid to die
|
||||||
|
pid_wait() {
|
||||||
|
declare cnt=0
|
||||||
|
|
||||||
|
while nullexec kill -0 "$1"; do
|
||||||
|
(( cnt >= service_stop_timeout )) && return 1
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
(( cnt++ ))
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## See if NAME is a function
|
||||||
|
is_function() {
|
||||||
|
declare name=$1 name_tupe
|
||||||
|
|
||||||
|
name_type=$( type -t "$name" )
|
||||||
|
|
||||||
|
if [[ $name_type == 'function' ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# DSL
|
||||||
|
depends() {
|
||||||
|
service_depends=( "$@" )
|
||||||
|
}
|
||||||
|
|
||||||
|
# Super functions
|
||||||
|
## Start the service, write down the svc pid
|
||||||
|
super_start() {
|
||||||
|
(( service_running )) && return 3
|
||||||
|
|
||||||
|
if (( service_managed )); then
|
||||||
|
if (( service_respawn )); then
|
||||||
|
svc respawn "${service_command[@]}" &>"$service_logfile" &
|
||||||
|
else
|
||||||
|
svc "${service_command[@]}" &>"$service_logfile" &
|
||||||
|
fi
|
||||||
|
|
||||||
|
svc_pid=$!
|
||||||
|
|
||||||
|
printf '%s' "$svc_pid" > "$svc_pidfile"
|
||||||
|
else
|
||||||
|
"${service_command[@]}" &
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## Reload the service
|
||||||
|
## Usually just sends HUP
|
||||||
|
super_reload() {
|
||||||
|
(( service_running )) || return 3
|
||||||
|
|
||||||
|
kill -n 1 "$service_pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
## Stop the service
|
||||||
|
## Returns:
|
||||||
|
## 3: Service is not running.
|
||||||
|
super_stop() {
|
||||||
|
(( service_running )) || return 3
|
||||||
|
|
||||||
|
nullexec kill -n "$service_stop_signal" "$service_pid" || return 1
|
||||||
|
pid_wait "$service_pid" || return 5
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Overloadable functions
|
||||||
|
start() { super_start; }
|
||||||
|
stop() { super_stop; }
|
||||||
|
restart() {
|
||||||
|
"$0" "$service_name" stop
|
||||||
|
"$0" "$service_name" start
|
||||||
|
}
|
||||||
|
|
||||||
|
# Code
|
||||||
|
main() {
|
||||||
|
# Let's set some defaults
|
||||||
|
service_managed=1
|
||||||
|
|
||||||
|
if (( $UID )); then
|
||||||
|
# XDG stuff
|
||||||
|
default XDG_CONFIG_HOME "$HOME/.config"
|
||||||
|
default XDG_RUNTIME_DIR "/run/user/$UID"
|
||||||
|
|
||||||
|
cfgdir="$XDG_CONFIG_HOME/sm"
|
||||||
|
rundir="$XDG_RUNTIME_DIR/sm"
|
||||||
|
logdir="$HOME/log/sm"
|
||||||
|
else
|
||||||
|
cfgdir='/etc/sm'
|
||||||
|
rundir='/run/sm'
|
||||||
|
logdir='/var/log/sm'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load custom functions
|
||||||
|
for f in "$cfgdir/functions"/*; do
|
||||||
|
source "$f" || die 9 "Failed to source functions from $f"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Now create the needed runtime stuff
|
||||||
|
for d in "$rundir" "$logdir"; do
|
||||||
|
mkdir -p "$d" || die 3 "Failed to create runtime dir: $d"
|
||||||
|
done
|
||||||
|
|
||||||
|
# service_name is just $1
|
||||||
|
service_name=$1
|
||||||
|
|
||||||
|
# Semi-hardcoded stuff
|
||||||
|
svc_pidfile="$rundir/$service_name.pid"
|
||||||
|
|
||||||
|
# Get the service defaults
|
||||||
|
[[ -f "$cfgdir/conf.d/$1" ]] && {
|
||||||
|
source "$cfgdir/conf.d/$1" || die 5 "Failed to read service defaults: $cfgdir/conf.d/$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the service config
|
||||||
|
source "$cfgdir/init.d/$1" || die 7 "Failed to read the service config: $cfgdir/init.d/$1"
|
||||||
|
|
||||||
|
# Legacy
|
||||||
|
[[ "$service_args" ]] && service_command=( "${service_command[@]}" "${service_args[@]}" )
|
||||||
|
[[ "$service_respawn" == 'true' ]] && service_respawn=1
|
||||||
|
|
||||||
|
[[ "$service_pidfile" ]] && service_managed=0
|
||||||
|
|
||||||
|
if ! (( service_managed )); then
|
||||||
|
(( service_respawn )) && die 21 "Refusing to respawn a service that manages itself."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Service-level defaults
|
||||||
|
default service_pidfile "$svc_pidfile"
|
||||||
|
default service_logfile "$logdir/$service_name.log"
|
||||||
|
default service_stop_timeout 30
|
||||||
|
default service_stop_signal 15
|
||||||
|
default service_reload_signal 1
|
||||||
|
|
||||||
|
# Let's see if there's a PID
|
||||||
|
if [[ -f "$service_pidfile" ]]; then
|
||||||
|
service_pid=$(<$service_pidfile)
|
||||||
|
|
||||||
|
# Let's see if it's running
|
||||||
|
if nullexec kill -0 "$service_pid"; then
|
||||||
|
service_running=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Check if action is even defined
|
||||||
|
is_function "$2" || die 17 "Function $2 is not defined for $service_name."
|
||||||
|
|
||||||
|
# Run pre_$action function
|
||||||
|
if is_function "pre_$2"; then
|
||||||
|
"pre_$2" || {
|
||||||
|
printf 'pre_%s failed!\n' "$2" || die 13
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the function
|
||||||
|
case "$2" in
|
||||||
|
stop)
|
||||||
|
printf 'Stopping %s... ' "$service_name"
|
||||||
|
|
||||||
|
stop
|
||||||
|
|
||||||
|
case "$?" in
|
||||||
|
0) printf 'ok.\n';;
|
||||||
|
3) printf 'not running.\n' "$service_name";;
|
||||||
|
5) printf 'timed out.\n';;
|
||||||
|
*) printf 'fail.\n';;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
|
||||||
|
start)
|
||||||
|
printf 'Starting %s... ' "$service_name"
|
||||||
|
|
||||||
|
start
|
||||||
|
|
||||||
|
case "$?" in
|
||||||
|
0) printf 'ok.\n';;
|
||||||
|
3) printf 'already running.\n';;
|
||||||
|
*) printf 'fail.\n';;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
|
||||||
|
*) "$2";;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run post_$action function
|
||||||
|
if is_function "post_$2"; then
|
||||||
|
"post_$2" || {
|
||||||
|
printf 'post_%s failed!\n' "$2" || die 15
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
Loading…
Reference in New Issue
Block a user