diff --git a/sm b/sm index ef87b1a..2e8b786 100755 --- a/sm +++ b/sm @@ -25,7 +25,7 @@ svc() { kill -n "$service_stop_signal" "$service_pid" pid_wait "$service_pid" && { - rm -f $svc_pidfile + rm -f $svc_pidfile $service_ready_flag } }; trap 'svc::cleanup' TERM INT @@ -33,27 +33,31 @@ svc() { 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=$! + while nullexec kill -n 0 "$service_pid"; do wait done } +#cat >/dev/null << EOF +## Respawn +respawn() { + respawn::cleanup() { + kill -n 15 $(jobs -p) + wait + exit 0 + }; trap 'respawn::cleanup' TERM + + while true; do + exec "$@" & + job_pid=$! + wait + done +} +#EOF + ## Run a command with its output discarded nullexec() { "$@" &>/dev/null @@ -64,10 +68,8 @@ pid_wait() { declare cnt=0 while nullexec kill -0 "$1"; do - (( cnt >= service_stop_timeout )) && return 1 - - sleep 1 - + (( cnt >= (service_stop_timeout*10) )) && return 1 + sleep 0.1 (( cnt++ )) done @@ -87,9 +89,54 @@ is_function() { return 1 } -# DSL -depends() { - service_depends=( "$@" ) +## Simple timer +timer() { + declare cnt timeout=$1 + shift + + while ! "$@"; do + (( cnt >= (timeout*10) )) && return 1 + sleep 0.1 + (( cnt++ )) + done + + return 0 +} + +## Is a service ready? +is_ready() [[ -f "$service_ready_flag" ]] + +## Wait for this service to get ready +wait_ready() { + timer "$service_ready_timeout" is_ready +} + +## Depend on other services to be started +depend() { + declare s + + for s in "$@"; do + if ! "$0" "$s" qstatus; then + nullexec "$0" "$s" start || { + failed_deps+=( "$s" ) + return 1 + } + fi + done +} + +## Depend on other services to be ready +depend_ready() { + declare s + + depend "$@" || return 1 + + for s in "$@"; do + "$0" "$s" wait_ready || { + failed_deps+=( "$s" ) + return 1 + } + done } # Super functions @@ -97,6 +144,11 @@ depends() { super_start() { (( service_running )) && return 3 + [[ -f "${service_command[0]}" ]] || return 9 + + depend "${service_depends[@]}" || return 7 + depend_ready "${service_depends_ready[@]}" || return 7 + if (( service_managed )); then if (( service_respawn )); then svc respawn "${service_command[@]}" &>"$service_logfile" & @@ -111,9 +163,20 @@ super_start() { "${service_command[@]}" & fi + if timer "$service_ready_timeout" ready; then + printf '1' > "$service_ready_flag" + else + return 5 + fi + return 0 } +# A separate function for oneshot services +super_oneshot() { + (( service_enabled )) && return 3 +} + ## Reload the service ## Usually just sends HUP super_reload() { @@ -137,11 +200,26 @@ super_stop() { # Overloadable functions start() { super_start; } stop() { super_stop; } +reload() { super_reload; } restart() { "$0" "$service_name" stop "$0" "$service_name" start } +logs() { cat "$service_logfile"; } + +## Status is a bit of a special case. It's talkative. +status() { + (( service_running )) && return 0 + return 1 +} + +## For use in scripts +qstatus() { nullexec status; } + +## By default there is no ready check +ready() { :; } + # Code main() { # Let's set some defaults @@ -199,8 +277,10 @@ main() { default service_pidfile "$svc_pidfile" default service_logfile "$logdir/$service_name.log" default service_stop_timeout 30 + default service_ready_timeout 15 default service_stop_signal 15 default service_reload_signal 1 + default service_ready_flag "$rundir/$service_name.ready" # Let's see if there's a PID if [[ -f "$service_pidfile" ]]; then @@ -212,14 +292,14 @@ main() { 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 + printf 'pre_%s failed!\n' "$2" + die 13 } fi @@ -228,9 +308,9 @@ main() { stop) printf 'Stopping %s... ' "$service_name" - stop + stop; res=$? - case "$?" in + case "$res" in 0) printf 'ok.\n';; 3) printf 'not running.\n' "$service_name";; 5) printf 'timed out.\n';; @@ -241,22 +321,51 @@ main() { start) printf 'Starting %s... ' "$service_name" - start + start; res=$? - case "$?" in + case "$res" in 0) printf 'ok.\n';; 3) printf 'already running.\n';; + 5) printf 'readyness check timed out.\n';; + 7) printf 'dependencies failed: %s.\n' "${failed_deps[@]}";; + 9) printf 'service_command does not exist: %s.\n' "${service_command[0]}";; *) printf 'fail.\n';; esac ;; - *) "$2";; + reload) + printf 'Reloading %s... ' "$service_name" + + reload; res=$? + + case "$res" in + 0) printf 'ok.\n';; + *) printf 'fail.\n';; + esac + ;; + + status) + status; res=$? + + case "$res" in + 0) printf '%s is running/enabled.\n' "$service_name";; + 1) printf '%s is not running/enabled.\n' "$service_name";; + *) printf '%s: status unknown.\n' "$service_name";; + esac + ;; + + logs) logs;; + + *) "$2"; res=$?;; esac + (( res )) && return "$res" + # Run post_$action function if is_function "post_$2"; then "post_$2" || { - printf 'post_%s failed!\n' "$2" || die 15 + printf 'post_%s failed!\n' "$2" + die 15 } fi }