#!/bin/bash # ### BEGIN INIT INFO # Provides: ${pkg.name} # Required-Start: $remote_fs $syslog $network # Required-Stop: $remote_fs $syslog $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: ${project.name} # Description: ${project.description} # chkconfig: 2345 99 01 ### END INIT INFO [[ -n "$DEBUG" ]] && set -x # Initialize variables that cannot be provided by a .conf file WORKING_DIR="$(pwd)" # shellcheck disable=SC2153 mainfile=${pkg.installFolder}/bin/${pkg.name} configfile=${pkg.name}.conf # Follow symlinks to find the real script and detect init.d script cd "$(dirname "$0")" || exit 1 [[ -z "$initfile" ]] && initfile=$(pwd)/$(basename "$0") while [[ -L "$initfile" ]]; do [[ "$initfile" =~ init\.d ]] && init_script=$(basename "$initfile") initfile=$(readlink "$initfile") cd "$(dirname "$initfile")" || exit 1 initfile=$(pwd)/$(basename "$initfile") done initfolder="$( (cd "$(dirname "initfile")" && pwd -P) )" cd "$WORKING_DIR" || exit 1 # Initialize CONF_FOLDER location [[ -z "$CONF_FOLDER" ]] && CONF_FOLDER="${pkg.installFolder}/conf" # shellcheck source=/dev/null [[ -r "${CONF_FOLDER}/${configfile}" ]] && source "${CONF_FOLDER}/${configfile}" # Initialize PID/LOG locations if they weren't provided by the config file [[ -z "$PID_FOLDER" ]] && PID_FOLDER="/var/run" [[ -z "$LOG_FOLDER" ]] && LOG_FOLDER="${pkg.unixLogFolder}" ! [[ "$PID_FOLDER" == /* ]] && PID_FOLDER="$(dirname "$mainfile")"/"$PID_FOLDER" ! [[ "$LOG_FOLDER" == /* ]] && LOG_FOLDER="$(dirname "$mainfile")"/"$LOG_FOLDER" ! [[ -x "$PID_FOLDER" ]] && PID_FOLDER="/tmp" ! [[ -x "$LOG_FOLDER" ]] && LOG_FOLDER="/tmp" # Set up defaults [[ -z "$MODE" ]] && MODE="auto" # modes are "auto", "service" or "run" [[ -z "$USE_START_STOP_DAEMON" ]] && USE_START_STOP_DAEMON="true" # Create an identity for log/pid files if [[ -z "$identity" ]]; then if [[ -n "$init_script" ]]; then identity="${init_script}" else identity=$(basename "${initfile%.*}")_${initfolder//\//} fi fi # Initialize log file name if not provided by the config file [[ -z "$LOG_FILENAME" ]] && LOG_FILENAME="${identity}.log" # ANSI Colors echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; } echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; } echoYellow() { echo $'\e[0;33m'"$1"$'\e[0m'; } # Utility functions checkPermissions() { touch "$pid_file" &> /dev/null || { echoRed "Operation not permitted (cannot access pid file)"; return 4; } touch "$log_file" &> /dev/null || { echoRed "Operation not permitted (cannot access log file)"; return 4; } } isRunning() { ps -p "$1" &> /dev/null } await_file() { end=$(date +%s) let "end+=10" while [[ ! -s "$1" ]] do now=$(date +%s) if [[ $now -ge $end ]]; then break fi sleep 1 done } # Determine the script mode action="run" if [[ "$MODE" == "auto" && -n "$init_script" ]] || [[ "$MODE" == "service" ]]; then action="$1" shift fi # Build the pid and log filenames if [[ "$identity" == "$init_script" ]] || [[ "$identity" == "$APP_NAME" ]]; then PID_FOLDER="$PID_FOLDER/${identity}" pid_subfolder=$PID_FOLDER fi pid_file="$PID_FOLDER/${identity}.pid" log_file="$LOG_FOLDER/$LOG_FILENAME" # Determine the user to run as if we are root # shellcheck disable=SC2012 [[ $(id -u) == "0" ]] && run_user=$(ls -ld "$mainfile" | awk '{print $3}') arguments=($RUN_ARGS "$@") # Action functions start() { if [[ -f "$pid_file" ]]; then pid=$(cat "$pid_file") isRunning "$pid" && { echoYellow "Already running [$pid]"; return 0; } fi do_start "$@" } do_start() { working_dir=$(dirname "$mainfile") pushd "$working_dir" > /dev/null mkdir -p "$PID_FOLDER" &> /dev/null if [[ -n "$run_user" ]]; then checkPermissions || return $? if [[ -z "$pid_subfolder" ]]; then chown "$run_user" "$pid_subfolder" fi chown "$run_user" "$pid_file" chown "$run_user" "$log_file" if [ $USE_START_STOP_DAEMON = true ] && type start-stop-daemon > /dev/null 2>&1; then start-stop-daemon --start --quiet \ --chuid "$run_user" \ --name "$identity" \ --make-pidfile --pidfile "$pid_file" \ --background --no-close \ --startas "$mainfile" \ --chdir "$working_dir" \ -- "${arguments[@]}" \ >> "$log_file" 2>&1 await_file "$pid_file" else su -s /bin/sh -c "$mainfile $(printf "\"%s\" " "${arguments[@]}") >> \"$log_file\" 2>&1 & echo \$!" "$run_user" > "$pid_file" fi pid=$(cat "$pid_file") else checkPermissions || return $? "$mainfile" "${arguments[@]}" >> "$log_file" 2>&1 & pid=$! disown $pid echo "$pid" > "$pid_file" fi [[ -z $pid ]] && { echoRed "Failed to start"; return 1; } echoGreen "Started [$pid]" } stop() { working_dir=$(dirname "$mainfile") pushd "$working_dir" > /dev/null [[ -f $pid_file ]] || { echoYellow "Not running (pidfile not found)"; return 0; } pid=$(cat "$pid_file") isRunning "$pid" || { echoYellow "Not running (process ${pid}). Removing stale pid file."; rm -f "$pid_file"; return 0; } do_stop "$pid" "$pid_file" } do_stop() { kill -2 "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; } for i in $(seq 1 60); do isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; } [[ $i -eq 30 ]] && kill -9 "$1" &> /dev/null sleep 1 done echoRed "Unable to kill process $1"; return 1; } restart() { stop && start } force_reload() { working_dir=$(dirname "$mainfile") pushd "$working_dir" > /dev/null [[ -f $pid_file ]] || { echoRed "Not running (pidfile not found)"; return 7; } pid=$(cat "$pid_file") rm -f "$pid_file" isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 7; } do_stop "$pid" "$pid_file" do_start } status() { working_dir=$(dirname "$mainfile") pushd "$working_dir" > /dev/null [[ -f "$pid_file" ]] || { echoRed "Not running"; return 3; } pid=$(cat "$pid_file") isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 1; } echoGreen "Running [$pid]" return 0 } run() { pushd "$(dirname "$mainfile")" > /dev/null "$mainfile" "${arguments[@]}" result=$? popd > /dev/null return "$result" } # Call the appropriate action function case "$action" in start) start "$@"; exit $?;; stop) stop "$@"; exit $?;; restart) restart "$@"; exit $?;; force-reload) force_reload "$@"; exit $?;; status) status "$@"; exit $?;; run) run "$@"; exit $?;; *) echo "Usage: $0 {start|stop|restart|force-reload|status|run}"; exit 1; esac exit 0