#!/bin/bash

#########################################################################################
# Tools used by all script invoked in update process
# Version should be the same as those used in update_survey script
#
# Versions
#
# 1.0.7
#
# Add some functions
#	- IsProcessRunning
#	- KillRunningProcess
#
# Add log levels for LogUpdate function
# 	- NORMAL / WARNING / SEVERE
#	- If no parameter given, then used a NORMAL level
#
# 1.0.8 
#
# Modify SetLed function to support more modes and colors
# Imply to modify also the calls from other files
#
# 1.0.9
# The script gets its own version and don't share the SCRIPT_VERSION variable anymore
#
# 1.1.0
# Add a verification to each directory creation in order to suppress a potential existing file
# with the same name which will avoid the directory creation
#
# 1.1.1
# Add definition of FAST_BLINK, NORMAL_BLINK and SLOW_BLINK for led blinking
#
# 1.1.2
# Adjust content of BACKUP_ENV_DIR variable to match the name used in update_survey script
#
# 1.2.0
# Add a specific part of logged msg so that a reader can clearly identify the emitter
# Integration EMSS hardware specificities
#
# 1.2.3
# Suppress the UC_Writelog function as it was not only unnecessary because it just did a 
# call to the LogUpdate function with all arguments received but also a source of problems 
# as the LogUpdate function called was placed below the UC_WriteLog function calling.
#
# 1.2.4
# Optimisation of LogUpdate function with the awk script output_filtered_log
# Gain : about 5x faster than the previous implementation
#########################################################################################

UPDATE_COMMON_SCRIPT_VERSION="1.2.4"
UPDATE_COMMON_SCRIPT_INITIALS="[UC-]"

#########################################################################################
# Local variables defined
#########################################################################################

UPDATE_OK=0
UPDATE_ABORT=1

CONSOLE=/dev/console

ROOT_DIR=/root

APP_DIR=$ROOT_DIR/app

BACKUP_APP_DIR=$ROOT_DIR/oldapp
BACKUP_ENV_DIR=$ROOT_DIR/backenv

MBSA_DIRECTORY=mbsa
OSGI_DIRECTORY=osgi

USR_LOCAL_BIN="/usr/local/bin"
HAGER_APP_STARTING_SCRIPT="application"

HAGER_APPLICATION="/usr/local/bin/application"
APP_BACKUP_FILE="txa100.backup.bak"

# -----------------	Update directories defines

UNZIP_DIR_OLD="/tmp/unzip"
UNZIP_DIR_NEW="/root/unzip"
UNZIP_DIR=$UNZIP_DIR_NEW

UPDATE_DIR="/tmp/update"

UPDATE_INSTALL_DIR=$UPDATE_DIR/install
UPDATE_STATE_DIR=$UPDATE_DIR/platformstate

UPDATE_EXTENSION=zip
UPDATE_STATUS_FILE=$UPDATE_DIR/status

COMMON_LIBRARY="update_common"

#------------------ Environment script parameter

MODENV_DIR=modify_environment
MODENV_SCRIPT=modify_environment.sh

FUNC_INSTALL="Install"
FUNC_RESTORE="Restore"
FUNC_CLEAN="Clean"

#------------------ Specific hardware defines

BOARDTYPE_FILE=/proc/HBox-BoardType

KNX_BOARD="KNX"
EMSS_BOARD="HBS"

KNX_LED_POWER=1
KNX_LED_KNX=2
KNX_LED_ETH0=3
KNX_LED_ETH1=4
KNX_LED_PORTAL=5

EMSS_LED_STATUS=1
EMSS_LED_PORTAL=2
EMSS_LED_ETH0=3
EMSS_LED_ETH1=4
EMSS_LED_POWER=5

#------------------ LogFiles defines

OUTPUT_FILTERED_LOG=/usr/local/bin/output_filtered_log
DATA_VAR_DIR=/data/var
DATA_VAR_LOG_DIR=$DATA_VAR_DIR/log
APP_LOGFILE=$DATA_VAR_LOG_DIR/update

LOG_DEBUG=1
LOG_WARNING=2
LOG_NORMAL=3

HBOX_LOG_LEVEL_STR="HBoxLogLevel"
LOGFILE_AND_CONSOLE="Console"

#------------------ Signal defines

SIGKILL=9
SIGTERM=15

#------------------ Leds defines

GREEN=green
RED=red
ORANGE=orange

LED_POWER=1
LED_KNX=2
LED_ETH1=4
LED_ETH0=3
LED_PORTAL=5

FAST_BLINK=100
NORMAL_BLINK=500
SLOW_BLINK=1000

#------------------ Status defines

STATUS_UPDATE_OK=0
STATUS_SURVEY=1
STATUS_UPDATE_ABORT=2
STATUS_UPDATE_FILE_ERROR=3
STATUS_WAIT_APP=4
STATUS_APP_STARTED=5
STATUS_UPDATE_FILE_OK=6

#########################################################################################
# Global variables 
#########################################################################################

updateStatus=$STATUS_SURVEY

#########################################################################################
# Functions
#########################################################################################

#########################################################################################
# Function that create a directory with a given name. Verify first that a file with the 
# same name doesn't exist at the designed location because it will prevent directory 
# creation. 
#
# Input - $1 Directory name

CreateDirectory() {
	
	if [ ! -z $1 ]; then 																											# The directory name must be given at the least 

		if [ ! -e "$1" ]; then																										# If directory already exists, just do nothing

			if [ -f "$1" ]; then 																									# Verify if a file with the same name already exists at current location
				rm "$1"																												# If so, it must be deleted, otherwise it will prevent the directory to be created
			fi

			mkdir "$1"
		fi
	fi
}

#########################################################################################
# Function that verify prensence of log directories and create them if needed

ManageLogDirectories() {

	if [ ! -e "$DATA_VAR_DIR" ]; then																								# Create log directories
		CreateDirectory $DATA_VAR_DIR																											# In case we must write log entries
	fi

	if [ ! -e "$DATA_VAR_LOG_DIR" ]; then																							# Without the help of UPDATE_COMMON_FILE
		CreateDirectory $DATA_VAR_LOG_DIR																										# As it is not present
	fi

}

#########################################################################################
# Function that log a message in logfile
#
# Input - $1 Message
# 		  $2 Severity level of the message : NORMAL - WARNING
# 		  $3 Destination : LOGFILE - LOGFILE + CONSOLE

LogUpdate() {

	# Create the log file and directories if needed
	ManageLogDirectories
	
	caller=`cat /proc/$$/cmdline | tr '\000' '#'`
	$OUTPUT_FILTERED_LOG -v caller=$caller -v comment="$1" -v logLevel=$2 >> $APP_LOGFILE

	# The third parameter (optional) allows the message to be displayed on the console

	if [ ! -z $3 ] && [ "$3" == "$LOGFILE_AND_CONSOLE" ]; then
		TimeElapsed=`awk '{printf("[%11f]", $1)}' /proc/uptime)`
		echo "$TimeElapsed $ScriptInitials $1" >> $CONSOLE
	fi
}

#########################################################################################
# Function that verify presence of some update directories and create them if needed

ManageUpdateDirectories() {

	if [ ! -d "$UPDATE_DIR" ] ; then																								# Check presence of UPDATE_DIR
		LogUpdate "Creating $UPDATE_DIR directory"																					# As it could be accidentally destroyed
		CreateDirectory $UPDATE_DIR																									# And then must be restored
	fi

	if [ ! -d "$UPDATE_INSTALL_DIR" ]; then																							# Do the same for UPDATE_INSTALL_DIR
		LogUpdate "Creating $UPDATE_INSTALL_DIR directory"																			# Which is the place where update file will be placed
		CreateDirectory $UPDATE_INSTALL_DIR
	fi
	
	if [ ! -d "$UPDATE_STATE_DIR" ]; then																							# And for UPDATE_STATE_DIR
		LogUpdate "Creating $UPDATE_STATE_DIR directory"																				# Which is the place to exchange files with the application
		CreateDirectory $UPDATE_STATE_DIR
	fi
}

#########################################################################################
# Function that retrieve the HBox type 

Get_BoardType() {

 	boardtype=$(cat $BOARDTYPE_FILE)
 	echo $boardtype
}

#########################################################################################
# Function that set correct defines according to the current board type

UC_InitSpecific() {
 	
 	boardtype=$(Get_BoardType)

 	case "$boardtype" in

 		$KNX_BOARD)
			LED_POWER=$KNX_LED_POWER
			LED_KNX=$KNX_LED_KNX
			LED_ETH1=$KNX_LED_ETH1
			LED_ETH0=$KNX_LED_ETH0
			LED_PORTAL=$KNX_LED_PORTAL
			;;

		$EMSS_BOARD)
			LED_POWER=$EMSS_LED_POWER
			LED_STATUS=$EMSS_LED_STATUS
			LED_ETH1=$EMSS_LED_ETH1
			LED_ETH0=$EMSS_LED_ETH0
			LED_PORTAL=$EMSS_LED_PORTAL
			;;

		*)
			;;

	esac
 }

#########################################################################################
# Function that manage designed led using given modes and colors 
#
# Input - $1 : led : POWER - KNX - ETH0 - ETH1 - PORTAL
# 		  $2 : color : GREEN - RED - ORANGE
# 		  $3 : mode : FIXED - NORMAL_BLINK - FAST_BLINK - SLOW_BLINK - HEARTBEAT

SetLed() {
	
	LogUpdate "Set Led $1 $2 $3"
	
	Led=""

	case "$1" in
		
		POWER)
			Led=$LED_POWER
			;;

		KNX | STATUS)
			if [ $boardtype == "$KNX_BOARD" ]; then
				Led=$LED_KNX
			else
				Led=$LED_STATUS
			fi
			;;

		ETH0)
			Led=$LED_ETH0
			;;

		ETH1)
			Led=$LED_ETH1
			;;

		PORTAL)
			Led=$LED_PORTAL
			;;

		*)
			LogUpdate "SetLed - Unsupported led : $1. Supported leds are : POWER - KNX - ETH0 - ETH1 - PORTAL"
			return 1
			;;

	esac

	GreenBrightness=0
	RedBrightness=0

	if [ $boardtype == "$EMSS_BOARD" ]; then
		OrangeBrightness=0
	fi

	case "$2" in 
	
		GREEN)
			GreenBrightness=1
			;;
		
		RED)
			RedBrightness=1
			;;
		
		ORANGE)
			
			if [ $boardtype == "$KNX_BOARD" ]; then
				GreenBrightness=1
				RedBrightness=1
			else
				OrangeBrightness=1
			fi

			;;

		LEDOFF)
			;;

		*)
			LogUpdate "SetLed - Unsupported color : $2. Supported colors are : RED - GREEN - ORANGE"
			return 1
			;;
	
	esac

	GreenMode=none
	RedMode=none

	if [ $boardtype == "$EMSS_BOARD" ]; then
		OrangeMode=none
	fi

	SpeedBlink=""

	case "$3" in
		
		FIXED)
			;;
			
		NORMAL_BLINK | FAST_BLINK | SLOW_BLINK)

			if [ $GreenBrightness -eq 1 ]; then
				GreenMode=timer
			fi

			if [ $RedBrightness -eq 1 ]; then
				RedMode=timer
			fi

			if [ $boardtype == "$EMSS_BOARD" ]; then
				
				if [ $OrangeBrightness -eq 1 ]; then
					OrangeMode=timer
				fi
			fi

			if [ $3 == NORMAL_BLINK ]; then
				SpeedBlink=$NORMAL_BLINK
			elif [ $3 == FAST_BLINK ]; then
				SpeedBlink=$FAST_BLINK
			else
				SpeedBlink=$SLOW_BLINK
			fi

			;;
		
		HEARTBEAT)

			if [ $GreenBrightness -eq 1 ]; then
				GreenMode=heartbeat
			fi

			if [ $RedBrightness -eq 1 ]; then
				RedMode=heartbeat
			fi

			if [ $boardtype == "$EMSS_BOARD" ]; then

				if [ $OrangeBrightness -eq 1 ]; then
					OrangeMode=heartbeat
				fi
			fi

			;;

		*)
			LogUpdate "SetLed - Unsupported mode : $3. Supported mode are : FIXED - NORMAL_BLINK - FAST_BLINK - SLOW_BLINK - HEARTBEAT"
			return 1
			;;
	
	esac

	# Set green led

	echo $GreenMode > /sys/class/leds/led-green-"$Led"/trigger
	echo $GreenBrightness > /sys/class/leds/led-green-"$Led"/brightness
			
	if [ $GreenMode == timer ]; then
		echo $SpeedBlink > /sys/class/leds/led-green-"$Led"/delay_on
		echo $SpeedBlink > /sys/class/leds/led-green-"$Led"/delay_off
	fi

	# Set red led

	echo $RedMode > /sys/class/leds/led-red-"$Led"/trigger
	echo $RedBrightness > /sys/class/leds/led-red-"$Led"/brightness	

	if [ $RedMode == timer ]; then
		echo $SpeedBlink > /sys/class/leds/led-red-"$Led"/delay_on
		echo $SpeedBlink > /sys/class/leds/led-red-"$Led"/delay_off
	fi


	if [ $boardtype == "$EMSS_BOARD" ]; then

		# Set orange led

		echo $OrangeMode > /sys/class/leds/led-orange-"$Led"0/trigger
		echo $OrangeBrightness > /sys/class/leds/led-orange-"$Led"0/brightness	

		if [ $OrangeMode == timer ]; then
			echo $SpeedBlink > /sys/class/leds/led-orange-"$Led"0/delay_on
			echo $SpeedBlink > /sys/class/leds/led-orange-"$Led"0/delay_off
		fi
	fi

}

#########################################################################################
# Function that cleans directories totally or not depending on parameters
# 
# Order of parameters
# $1 = directory to clean

CleanDirectory() {

	if [ -d "$1" ] ; then																											# Check presence of given directory
		LogUpdate "Cleaning $1 directory"
		rm -Rf $1 2>&1 >> $APP_LOGFILE
	fi
}

#########################################################################################
# Input : $1 process name or process pid
# Output : String with pid if some

GetPidOfRunninProcess() {

	pids=$(ps | grep -v -e $0 -e grep | grep "$1" | head -n 1 | awk '{printf $1}')
	echo $pids
}


#########################################################################################
# Function that return a list of the PIDs of all processes whose name is given on entry
# It use grep instead of pidof as it can find some processes that pidof will not
#
# Input  : $1 Process name
# Output : String

GetRunningIds() {
	psfound=$(ps | grep -v -e $0 -e grep | grep -i "$1" | awk '{print $1}')
	echo $psfound
}

#########################################################################################
# Function that return the PPid of a process if this one exists
#
# Input : $1 - Process's id about which we will return the parent id
# Output : Parent Id

GetProcessParentId() {

	if [ -z "$1" ]; then
		LogUpdate "ERROR ----- GetProcessParentId need an id as parameter. Abort"
		return 1
	fi

	if [ ! -f "/proc/$1/status" ]; then
		LogUpdate "ERROR ----- Can't find a /proc/$1/status file for given pid : $1. Abort"
		return 1
	fi

	val=$(cat /proc/$1/status | grep -i "ppid" | awk '{print $2}')

	echo $val
	return 0
}

#########################################################################################
# Function that check during time-out if given pid is not running anymore
# Input : $1 - Process pid to check

DidProcessDied() {

	bContinue=true
	Count=1
	MAX_TRIES=101

	while [ $Count -lt $MAX_TRIES ] && [ $bContinue == true ]; do
		
		mypid=$(GetPidOfRunninProcess "$1")
		
		if [ -z $mypid ]; then
			LogUpdate "Process pid : $1 is not running anymore" $LOG_DEBUG
			bContinue=false
			break
		fi

		usleep 1000													# 1 millisecond
		Count=`expr $Count + 1`

	done

	if [ $bContinue == true ]; then
		return 1
	fi

	LogUpdate "Took less than $Count milliseconds to kill $1 process" $LOG_DEBUG
	return 0
}

#########################################################################################
# Function that try to detect a running process whose name is given on entry using ps 
# and grep tools
# 
# Input  - $1 Process name
# Output - 0 : Process found / 1 : Process not found

IsProcessRunning() {

	psfound=$(GetPidOfRunninProcess "$1")

	if [ -z $psfound ]; then
		return 1
	else
		return 0
	fi
}

#########################################################################################
GentlyKillProcess() {
	LogUpdate "Kill process $1 using SIGTERM ($SIGTERM) signal" $LOG_DEBUG
	kill -$SIGTERM $1 2>&1 >> $APP_LOGFILE
}

#########################################################################################
HardlyKillprocess() {
	LogUpdate "Kill process $1 using SIGKILL ($SIGKILL) signal" $LOG_DEBUG
	kill -$SIGKILL $1 2>&1 >> $APP_LOGFILE
}

#########################################################################################
# Function that kill a process whose pid is given in entry
#
# Input : $1 process pid
# Output : 0 - OK / 1 - Not OK

KillRunningPid() {

	if [ ! -z $1 ]; then

		pid=$1

		GentlyKillProcess $pid

		if ! DidProcessDied $pid; then

			LogUpdate "Process pid:$pid can't be gently killed. Hard way now !" $LOG_WARNING
			HardlyKillprocess $pid

			if ! DidProcessDied $pid; then
				LogUpdate "ERROR ----- Process pid:$pid id can't be killed. Abort"
				return 1
			fi
		fi

		LogUpdate "Process pid:$pid has been killed"

	else
		LogUpdate "KillRunningPid : Missing argument. Process pid expected"
		return 1
	fi

	return 0
}

#########################################################################################
# Function that kill the first running processes that have the name given on entry
#
# Input : $1 Process name
# Output : 0 - OK / 1 - Not OK

KillRunningProcess() {

	if [ -z $1 ]; then
		LogUpdate "KillRunningProcess : Missing argument. Process name expected"
		return 1
	fi

	psfound=$(GetPidOfRunninProcess "$1")
	
	if ! KillRunningPid $psfound; then
		return 1
	fi
	
	return 0
}

#########################################################################################
# Functions that use either pidof or ps to retrieve a process whose name is given in 
# entry in order to kill it
#
# Input  : $1 -> Process name
# Output : none

KillProcessByPidof() {
	
	if [ ! -z "$1" ]; then

		LogUpdate "Look at $1 process using pidof identification" $LOG_WARNING
		pidfound=$(pidof "$1") 2>&1 >> $APP_LOGFILE

		if [ ! -z $pidfound ]; then
			LogUpdate "Killing $1 process" $LOG_WARNING
			kill -9 $pidfound
			LogUpdate "Killing done" $LOG_WARNING
		else
			LogUpdate "No process to kill" $LOG_WARNING
		fi

	else
		LogUpdate "KillProcessByPidof - Missing argument : process name"
	fi
}

#########################################################################################

KillProcessByPS() {

	if [ ! -z "$1" ]; then

		LogUpdate "Look at $1 process using ps identification" $LOG_WARNING
		psfound=$(ps | grep -v -e $0 -e grep | grep "$1" | awk '{print $1}')

		if [ ! -z $psfound ]; then
			LogUpdate "Killing $1 process" $LOG_WARNING
			kill -9 $psfound
			LogUpdate "Killing done" $LOG_WARNING
		else
			LogUpdate "No process to kill" $LOG_WARNING
		fi

	else
		LogUpdate "KillProcessByPS - Missing argument : process name"
	fi
}

#########################################################################################
# Function for setting internal status that will be used by all update sequence logic
#
# Order of parameters
# $1 Status to set 

SetUpdateStatus() {
	if [ ! -z "$1" ]; then
		echo "$1" > $UPDATE_STATUS_FILE
	fi
}

#########################################################################################
# Function to get internal status that will be used by all update sequence logic

GetUpdateStatus() {
	updateStatus=$(cat $UPDATE_STATUS_FILE)
}

#########################################################################################
CollectScriptVersion() {

	patterntofind="version="
	currentVersion="No version"

	# Check argument presence
	if [ -z "$1" ]; then
		LogUpdate "CollectScriptVersion : Missing argument : FilePathName"

	else

		# Check file existence
		if [ ! -e "$1" ]; then
			LogUpdate "CollectScriptVersion : $1 doesn't exists"
		else

			# OK, file exists, check version
			currentFile="$1"

			# Collect from file all lines with "version=" pattern
			extractStr=$(grep -i "$patterntofind" "$currentFile")

			# But just keep the first one which is those we are looking for
			extract=$(echo $extractStr | cut -d" " -f1)

			if [ ! -z $extract ]; then
				currentVersion=$(echo "$extract" | cut -d "=" -f2)
			fi

		fi
	fi

	# Delete potential quotation marks at the ends of the string
	lenVersion=${#currentVersion}

	if [ $(echo $currentVersion | tail -c 2) == '"' ]; then
		let lenVersion--
	fi
	
	if [ $(echo $currentVersion | head -c 1) == '"' ]; then
		let lenVersion--
		currentVersion=${currentVersion:1:$lenVersion}
	else
		currentVersion=${currentVersion:0:$lenVersion}
	fi
	

	# return string
	echo "$currentVersion"
}

#########################################################################################
#########################################################################################
# Script entry
#########################################################################################
#########################################################################################

UC_InitSpecific
