Skip to content
Snippets Groups Projects
initd.sh 5.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • #!/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)
    
    if [ $PATH == '' ]; then
      PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    fi
    
    #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 $?