#!/bin/sh

. /etc/profile

# NUC Brake mechanism wrapper - slows doen heavy I/O commands for overall better system performance and avoid timeouts of other processes
# 
# Author: mihai-radu.dumtiru.ext@unify.com
#
# Version
# 2017-02-11: V0.1 initial creation


# Sample usage:
# 
# Synchronous call - will return the RC of the command :
# 
#    nuc_brake run-interval=2,stall-interval=6 gunzip -c img.gz > img
# 
# In background with delay-start (both optional) - in case fork is used, then the return code will not be returned : 
# 
#    nuc_brake run-interval=2,stall-interval=6,fork,delay-start=30 gunzip -c img.gz > img
#
# Note: run-interval and stall-interval are always mandatory in the commandline

## MAIN ##

########### echo "$0 $$ >>>>"
########### set | grep _NUC_BRAKE
########### echo "$0 $$ <<<<"
########### sleep 10

[ -z "$_ENV_KILL_IF__ENV2_" ] && {
	export _ENV_KILL_IF__ENV2_=mdazabcd$$
	
	#we are here in the main process so parse here the cmdline
	_nuc_brake_options=$1
	[[ "x$_nuc_brake_options" =~ "run-interval" ]] || {
		echo "nuc_brake error: parameter run-interval is mandatory."
		exit 1
	}
	[[ "x$_nuc_brake_options" =~ "stall-interval" ]] || {
		echo "nuc_brake error: parameter stall-interval is mandatory."
		exit 1
	}
	export _NUC_BRAKE_DELAY_START=0
	export _NUC_BRAKE_SYNC_RUN=true
	while [ -n "$_nuc_brake_options" ]; do
		if [[ $_nuc_brake_options =~ "," ]]; then
			_nuc_brake_co=${_nuc_brake_options%%,*}
			_nuc_brake_options=${_nuc_brake_options#*,}
		else
			_nuc_brake_co=$_nuc_brake_options
			_nuc_brake_options=""
		fi
		#echo "DBG: parsing option $_nuc_brake_co"
		_nuc_brake_costr=${_nuc_brake_co%=*}
		_nuc_brake_coval=${_nuc_brake_co#*=}
		case $_nuc_brake_costr in
			run-interval)
				export _NUC_BRAKE_INTERVAL_TO_RUN=$_nuc_brake_coval
				;;
			stall-interval)
				export _NUC_BRAKE_INTERVAL_TO_SLEEP=$_nuc_brake_coval
				;;
			fork)
				export _NUC_BRAKE_SYNC_RUN=false
				;;
			delay-start)
				export _NUC_BRAKE_DELAY_START=$_nuc_brake_coval
				;;
			*)
				echo "nuc_brake error: unknown option $_nuc_brake_costr"
				exit 1
				;;
		esac
	done
	shift
	export _NUC_BRAKE_CMDLINE="$*"
	
	#launch daemon
	(
		export _NUC_BRAKE_IN_DAEMON=$_ENV_KILL_IF__ENV2_
		setsid /bin/bash $0
	) &
	
	#launch watcher
	(
		export _NUC_BRAKE_IN_WATCH_PA=$_ENV_KILL_IF__ENV2_
		setsid /bin/bash $0
	) &
	
	_nuc_brake_rc=0
	$_NUC_BRAKE_SYNC_RUN && {
		wait
		_nuc_brake_rc=`cat /dev/_nuc_brake_rc.$_ENV_KILL_IF__ENV2_`
		rm /dev/_nuc_brake_rc.$_ENV_KILL_IF__ENV2_
	}
	exit $_nuc_brake_rc
}

function killifenv() {
	#send kill signal to the pid only if special environment variable is set
	local origcallparams="$*"
	set -- $*
	local pidnr=$_
	local pidenvfile="/proc/${pidnr}/environ"
	[ -e $pidenvfile ] || return 62
	grep -q _ENV_KILL_IF__ENV2_ $pidenvfile && {
		kill $origcallparams 1>/dev/null 2>/dev/null
		return $?
	}
	#pid does not belong to us
	echo "nuc_brake [$$] [$_ENV_KILL_IF__ENV2_] warning : killifenv called to signal pid $pidnr which does not belong to this session" 1>&2
	return 63
}

[ -n "$_NUC_BRAKE_IN_DAEMON" -a "$_NUC_BRAKE_IN_DAEMON" = "$_ENV_KILL_IF__ENV2_" ] && {
	# wait an initial time before starting
	sleep $_NUC_BRAKE_DELAY_START
	sesspid=`ps --no-headers -o sess -p $$ | tr -d [:space:]`
	echo $sesspid > /dev/_nuc_brake_session.$_ENV_KILL_IF__ENV2_
	_nuc_brake_start=`date +%s`
	_nuc_cmd_prefix="nice -n 10 ionice -c 3 "
    
    #OSFOURK-22156 RLC V10 R1.34 - Activation Preparation takes too long - 1h 49min on Simplex Eco1
    # do NOT use nocache on V10R1 as it is not needed - will cause Prep and container reimport to be too slow
	#if which nocache 1>/dev/null 2>/dev/null; then
	#	_nuc_cmd_prefix="$_nuc_cmd_prefix nocache "
	#fi
    
	$_nuc_cmd_prefix $_NUC_BRAKE_CMDLINE
	_nuc_brake_rc=$?
	$_NUC_BRAKE_SYNC_RUN && echo $_nuc_brake_rc > /dev/_nuc_brake_rc.$_ENV_KILL_IF__ENV2_
	#artificial sleep in case wrapper is used for commands which take too short time so that watcher can not identify the daemon
	while [ 1 ]; do
		[ $((`date +%s` - $_nuc_brake_start)) -gt 3 ] && break
		sleep 1
	done
	rm -f /dev/_nuc_brake_session.$_ENV_KILL_IF__ENV2_
	exit 0
}

[ -n "$_NUC_BRAKE_IN_WATCH_PA" -a "$_NUC_BRAKE_IN_WATCH_PA" = "$_ENV_KILL_IF__ENV2_" ] && {
	sleep $_NUC_BRAKE_DELAY_START
	#wait max 30 seconds for pid file
	thesession=""
	for i in {0..30}; do
		sleep 1
		[ -e /dev/_nuc_brake_session.$_ENV_KILL_IF__ENV2_ ] && {
			thesession=`cat /dev/_nuc_brake_session.$_ENV_KILL_IF__ENV2_`
			[ "x$thesession" = "x" ] && continue
			[ $thesession -eq $thesession ] && break
		}
	done
	thesession=`cat /dev/_nuc_brake_session.$_ENV_KILL_IF__ENV2_`
	[ "x$thesession" = "x" ] && exit 1
	[ $thesession -eq $thesession ] || exit 1
	#for as long as the pid file exists do the pausing
	while killifenv -0 $thesession; do
		pkill -STOP -s $thesession
		sleep $_NUC_BRAKE_INTERVAL_TO_SLEEP
		killifenv -0 $thesession || exit 0
		pkill -CONT -s $thesession
		sleep $_NUC_BRAKE_INTERVAL_TO_RUN
	done
	exit 0
}

echo "nuc_brake [$$] error : should not have landed here."
exit 99
