From 2b72e15884c90cf9bab5950695ceeefc1bb9e93a Mon Sep 17 00:00:00 2001 From: fbt Date: Sun, 13 Nov 2016 11:11:47 +0300 Subject: [PATCH] init Signed-off-by: fbt --- sm | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100755 sm diff --git a/sm b/sm new file mode 100755 index 0000000..ef87b1a --- /dev/null +++ b/sm @@ -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 "$@"