#!/bin/bash
### BEGIN INIT INFO
# Provides: {{ item.service }}
# Required-Start:    $network $remote_fs $local_fs
# Required-Stop:     $network $remote_fs $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: forever-service startup script for {{ item.service }}
# Description: forever-service startup script for node script based service {{ item.service }}, uses forever to start the service
### END INIT INFO

#	CLI {{ item.script|default('app.js') }}
#	Working Directory {{ item.cwd }}

#this is to ensure forever is able to find out the correct root every time
export FOREVER_ROOT="/root/.forever"
FOREVER=$(which forever)

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

#Setup Environment variables (if any)
{% for export in item.envVarsArray|default([]) %}
export {{ export.key }}={{ export.value }}
{% endfor %}

# Check if any of $pid (could be plural) are running
LOGFILE="/var/log/nodejs/{{ item.service }}.log"
LOCKFILE="/var/lock/{{ item.service }}"
# introduce some gaps between restarts and throttle continous restarts
MIN_UPTIME="{{ item.minUptime|default('5000') }}"
SPIN_SLEEP_TIME="{{ item.spinSleepTime|default('2000') }}"
NODE_BIN_DIR="/usr/bin"
NODE_PATH="/usr/lib/node_modules"

# Add node to the path for situations in which the environment is passed.
PATH=$NODE_BIN_DIR:$PATH
export PATH=$PATH

# Export all environment variables that must be visible for the Node.js
# application process forked by Forever. It will not see any of the other
# variables defined in this script.
export NODE_PATH=$NODE_PATH

# kill signal: Since default needs to be SIGTERM, it is important that services gracefully shutdown,
# specially if they are doing transactions or other work which should not be interuppted in between
# for exceptional situation where you dont care about abrupt shutdown, SIGKILL should be used
KILL_SIGNAL="{{ item.killSignal|default('SIGTERM') }}"

# Wait time afer with SIGKILL will be sent to the process, in case SIGTERM is not fully finished
# This is required since when we use SIGTERM, some times if there is problem in code, it might take lot of time for process to exit
# or process may never exit, in such siutation we have to forcebly kill it so that shutdown or service restart can be done appropriately
# this wait time is in millisecond
KILLWAITTIME={{ item.forceKillWaitTime|default('5000') }}

killtree() {
  local _pid=$1
  local _sig=${2:--TERM}
  kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
  for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
    killtree ${_child} ${_sig}
  done
  kill -${_sig} ${_pid}
}

checkpidexists() {
	[ -d "/proc/$1" ] && return 0
	return 1
}

start() {
	STATUS=$($FOREVER --plain list | sed 's/data:\(\s*\[[0-9]*\]\s*\({{ item.service }}\)\s.*\)/\2-status:\1/;tx;d;:x')
	if ! [ -z "$STATUS" ]; then
		echo "Service {{ item.service }} already running"
		return 0
	fi

	echo  "Starting {{ item.service }}"

	# move to the directory from where the inital forever script was launched so that even if it is relative it works as expected
	cd {{ item.cwd }}

{% if item.applyUlimits|default(false) %}
	ulimit -f unlimited
	ulimit -t unlimited
	ulimit -v unlimited
	ulimit -n 64000
	ulimit -m unlimited
	ulimit -u 32000
{% endif %}

	$FOREVER \
    -a \
    -l $LOGFILE \
    --minUptime $MIN_UPTIME \
    --spinSleepTime $SPIN_SLEEP_TIME \
    --killSignal $KILL_SIGNAL \
    {{ item.foreverOptions|default('') }} \
    --uid {{ item.service }} \
    --workingDir {{ item.cwd }} \
    start {{ item.script|default('app.js') }} {{ item.scriptOptions|default('') }} 2>&1 >/dev/null
	RETVAL=$?

 	[ $RETVAL = 0 ] && touch $LOCKFILE
 	return $RETVAL
}

stop() {
	echo -n "Shutting down {{ item.service }}: "

	STATUS=$($FOREVER --plain list | sed 's/data:\(\s*\[[0-9]*\]\s*\({{ item.service }}\)\s.*\)/\2-status:\1/;tx;d;:x')
	if [ -z "$STATUS" ]; then
		echo "Not running"
		return 0
	fi

	PID=$($FOREVER --plain list | sed -n -e '/data:\s*\[[0-9]*\]\s\({{ item.service }}\)\s/p' | awk '{print $7}')
	if [ -z "$PID" ]; then
		echo "Could not get pid"
		return 0
	fi

	#run in background, since recent changes in forever, now blocks stop call with SIGTERM is finished
	#but we want to wait till some time and forcibly kill after elapsed time
	#without background script, we could be waiting forever
	$FOREVER stop {{ item.service }} 2>&1 >/dev/null &

	CURRENTWAITTIME=$KILLWAITTIME
	# wait for some time before forcefully killing the process
	while [ $CURRENTWAITTIME -gt 0 ]; do
		#check if the process is still running
		checkpidexists $PID
		if [ $? -ne 0 ]; then
			# if not running we can break, since no more wait is needed, service is stopped
			echo "Successful"
			break
		fi
	 	sleep 1
	 	CURRENTWAITTIME=$(( $CURRENTWAITTIME - 1000))
	done
	checkpidexists $PID
	if [  $? -eq 0  ]; then
		killtree $PID 9
		echo 'Forced shutdown'
	fi

	rm -f $LOCKFILE 2>&1 >/dev/null
	return 0
}

status() {
	STATUS=$($FOREVER --plain list | sed 's/data:\(\s*\[[0-9]*\]\s*\({{ item.service }}\)\s.*\)/\2-status:\1/;tx;d;:x')
	if [ -z "$STATUS" ]; then
		echo "{{ item.service }} is not running"
		RETVAL=3
	else
		echo $STATUS
		RETVAL=0
	fi
	return $RETVAL
}

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status
    ;;
  restart)
    stop
    start
    ;;
  *)
    echo "Usage: {{ item.service }} {start|stop|status|restart}"
    exit 1
    ;;
esac
exit $?
