#!/bin/bash # Version: 0.5.0. # Variant: service. # Copyright (c) 2005-2023: # Darren 'Tadgy' Austin # Licensed under the terms of the GNU General Public License version 3. SERVICE_EXEC="/usr/sbin/samba" SERVICE_ARGS=() SERVICE_ENV=() SERVICE_PIDFILE="/run/samba.pid" # Allow configuration in /etc/default to override. # Additional available variables: # SERVICE_EXTRA_ARGS=() # Extra arguments passed to $SERVICE_EXEC. Must be an array. # SERVICE_EXTRA_ENV=() # Extra environment passed to $SERVICE_EXEC. Must be an array. # SLAY_DELAY="" # Delay between the SIGTERM and SIGKILL on a 'stop'. Default: 2s. # RESTART_DELAY="" # Delay between stopping and starting on a 'restart'. Default: 2s. [[ -e "/etc/default/${0##*rc.}" ]] && { # shellcheck disable=SC1090 source "/etc/default/${0##*rc.}" 2>/dev/null || { printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "failed sourcing '/etc/default/${0##*rc.}'" >&2 exit 2 } } # Now all possible variable changes are complete, expand out the embedded variables, if any. eval "$(declare -p SERVICE_EXEC SERVICE_ARGS SERVICE_ENV SERVICE_PIDFILE SERVICE_EXTRA_ARGS SERVICE_EXTRA_ENV \ SLAY_DELAY RESTART_DELAY 2>/dev/null | sed -re 's/\\\$/$/g')" || { printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "failed expanding embedded variables" >&2 exit 2 } # This function can be used to perform any pre-start tests; hopfully to insure the daemon can start # correctly, before actually trying to start it. checkconfigured() { # Returns: # 0 - The pre-start tests passed and the service should be started. # >0 - Pre-start tests failed - the service should not be started. return 0 } checkstatus() { # Returns: # 0 - Service is running. # 1 - Service is running, but there is a problem with the .pid file. # 2 - Service is not running. # shellcheck disable=SC2155 local RET=0 RUNPIDS="$({ pgrep -f "$SERVICE_EXEC"; pgrep -F "$SERVICE_PIDFILE" 2>/dev/null; } | sort -u)" if [[ -n "$RUNPIDS" ]]; then printf "%s: %s: %s" "${BASH_SOURCE[0]##*/}" "status" "service running" if [[ -n "$SERVICE_PIDFILE" ]]; then if [[ ! -e "$SERVICE_PIDFILE" ]]; then printf "%s" ", but .pid file does not exist" RET=1 elif ! grep "\<$(<"$SERVICE_PIDFILE")\>" <<<"$RUNPIDS" >/dev/null 2>&1; then printf "%s" ", but .pid file is stale" RET=1 fi fi printf "\\n" else printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "status" "service stopped" RET=2 fi return "$RET" } startdaemon() { # Returns: # 0 - Service started successfully. # 1 - Sanity or pre-start check failed. # 2 - Error code returned when starting service. local ERR if [[ ! -e "$SERVICE_EXEC" ]]; then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "not started - '$SERVICE_EXEC' not found" >&2 return 1 elif [[ ! -x "$SERVICE_EXEC" ]]; then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "not started - '$SERVICE_EXEC' not executable" >&2 return 1 elif ! checkconfigured; then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "not started - pre-start checks failed" >&2 return 1 fi mkdir /run/samba 2>/dev/null env "${SERVICE_ENV[@]@Q}" "${SERVICE_EXTRA_ENV[@]@Q}" "$SERVICE_EXEC" "${SERVICE_ARGS[@]@Q}" "${SERVICE_EXTRA_ARGS[@]@Q}" >/dev/null 2>&1 ERR=$? (( ERR != 0 )) && { printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "failed to start service (error code '$ERR)'" >&2 return 2 } return 0 } stopdaemon() { # Returns: # 0 - The service was stopped sucessfully with a SIGTERM. # 1 - The service was stopped sucessfully with a SIGKILL. # 2 - An error occured while stopping service. local ERR RET=0 T="${SLAY_DELAY:-2}" kill -TERM "$(pgrep -f "$SERVICE_EXEC" | tr $'\n' ' ')" >/dev/null 2>&1 sleep 0.5 T="$(awk -v T="$T" 'BEGIN {print T - 0.5}')" checkstatus >/dev/null ERR=$? if (( ERR <= 1 )); then [[ -n "$SERVICE_PIDFILE" ]] && [[ -e "$SERVICE_PIDFILE" ]] && { kill -TERM "$(<"$SERVICE_PIDFILE")" >/dev/null 2>&1 sleep 0.5 T="$(awk -v T="$T" 'BEGIN {print T - 0.5}')" checkstatus >/dev/null ERR=$? (( ERR <= 1 )) && sleep "$T" } elif (( ERR == 0 )); then if kill -KILL "$({ pgrep -f "$SERVICE_EXEC"; cat "$SERVICE_PIDFILE" 2>/dev/null; } | sort -u | tr $'\n' ' ')" >/dev/null 2>&1; then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "warning" "failed to stop service gracefully - slayed" >&2 RET=1 else RET=2 fi fi return "$RET" } RET=0 case "$1" in 'start') checkstatus >/dev/null ERR=$? if (( ERR <= 1 )); then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "service running" >&2 printf "%s\\n" "Try: ${BASH_SOURCE[0]} status" >&2 RET=1 elif (( ERR == 2 )); then startdaemon ERR=$? if (( ERR == 1 )); then RET=3 elif (( ERR == 2 )); then RET=5 fi fi ;; 'stop') checkstatus >/dev/null ERR=$? if (( ERR <= 1 )); then stopdaemon ERR=$? (( ERR == 2 )) && { sleep 0.1 checkstatus >/dev/null ERR=$? (( ERR <= 1 )) && { printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "service failed to stop" >&2 RET=5 } } elif (( ERR == 2 )); then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "service stopped" >&2 printf "%s\\n" "Try: ${BASH_SOURCE[0]} status" >&2 RET=1 fi ;; 'restart') checkstatus >/dev/null ERR=$? (( ERR <= 1 )) && { stopdaemon ERR=$? if (( ERR <= 1 )); then sleep "${RESTART_DELAY:-2}" RET=-1 elif (( ERR == 2 )); then sleep 0.1 checkstatus >/dev/null ERR=$? (( ERR <= 1 )) && { printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "error" "service failed to stop" >&2 RET=5 } fi } (( RET <= 0 )) && { startdaemon ERR=$? if (( ERR == 0 )); then RET=0 elif (( ERR == 1 )); then RET=3 elif (( ERR == 2 )); then RET="$(( RET + 5 ))" fi } ;; 'status') checkstatus ERR=$? if (( ERR == 1 )); then RET=4 elif (( ERR == 2 )); then RET=5 fi ;; *) printf "%s\\n" "Usage: ${BASH_SOURCE[0]} " >&2 printf "%s\\n" "Error codes:" >&2 printf " %s: %s\\n" "0" "For 'start', 'restart' and 'stop': Requested action succeeded." >&2 printf " %s\\n" "For 'status': Service is running." >&2 printf " %s: %s\\n" "1" "Usage error." >&2 printf " %s: %s\\n" "2" "A general error occurred." >&2 printf " %s: %s\\n" "3" "Sanity or pre-start check failed." >&2 printf " %s: %s\\n" "4" "For 'start', 'restart' and 'stop': Partial failure to complete action." >&2 printf " %s\\n" "For 'status': Service is running, but a problem exists with the .pid file." >&2 printf " %s: %s\\n" "5" "For 'start', 'restart' and 'stop': Total failure to complete action." >&2 printf " %s\\n" "For 'status': Service is not running." >&2 RET=1 ;; esac exit "$RET"