#!/bin/sh
#######################################################################
# USBUPDATE install Script
#
# 1. Parameter: name of the tgz packet file
# 2. Parameter: update mode web or usb, source of the packet
# 3. Parameter: Output-Logfile (opt.)
#
# copyright (c) Berghof Automation GmbH
#######################################################################

# Umgebung einrichten

PATH=/bin:/sbin:/usr/bin:/usr/sbin:${PATH}
PACKAGEFILE=$(realpath ${1})
UPDATEMODE=${2}
PACKAGEPATH=$(realpath $(dirname ${PACKAGEFILE}))
UPDATECONFIG=${PACKAGEPATH}/usbupdate.ini
UPDATESTATE=${PACKAGEPATH}/usbupdatestate.ini
SYSCONFIG=/usr/etc/settings.ini
SKIPFWUPDATEFLAGFILE=/usr/local/etc/usb_fw_update.done
USBUPDATE_VERSION=$(cat pkgdesc.ini | sed -n "/\\[package\\]/,/\\[.*\\]/{/version[ ]*=/{s/^.*=//p}}")
DEVICETYPE=$(inigetkey "/etc/fwinfo.ini" firmware devicetype)
if test "${3}"; then
	LOGFILE=${3}
else
	LOGFILE=/tmp/pkginstall.log
fi

if test "${DEVICETYPE}" != "plc"; then
	FONTDESTINATION=/usr/share/fonts/X11/userfonts
else
	FONTDESTINATION=/home/plc/applications/fonts
fi

# Handle undefined variables as error (Debug option)
set -u

printlog()
{
	echo "USBUPDATE: $*"
	echo "USBUPDATE: $*" >> ${LOGFILE}
}

exitWithError ()
{
	printlog "ERROR: $*"
	exit 1
}

exitOk ()
{
	printlog "OK: $*"
	exit 0
}

compare_fw_version()
{
	# Diese Funktion vergleicht die Firmware Version des Systems
	# mit den uebergebenen Parametern
	# 
	#                 $1|$2|$3
	# Bsp: fw_version  1. 1. 0
	#
	# Return: 	1 = Versionen sind identisch
	#			0 = aktuelle System Version ist kleiner
	#			2 = aktuelle System Version ist groesser 
	
	fwVerMaj=$(inigetkey /etc/fwinfo.ini firmware version | awk -F'.' '{print '\$1'}')
	fwVerMin=$(inigetkey /etc/fwinfo.ini firmware version | awk -F'.' '{print '\$2'}')
	fwVerBug=$(inigetkey /etc/fwinfo.ini firmware version | awk -F'.' '{print '\$3'}')
	

	if [ $fwVerMaj -gt $1 ]; then
		return 2;
	elif [ $fwVerMaj -lt $1 ]; then
		return 0;
	else
		if [ $fwVerMin -gt $2 ]; then
			return 2;
		elif [ $fwVerMin -lt $2 ]; then
			return 0;
		else
			if [ $fwVerBug -gt $3 ]; then
				return 2;
			elif [ $fwVerBug -lt $3 ]; then
				return 0;
			else
				return 1;
			fi
		fi
	fi
}

write_update_stat_lastboot()
{
	DOCHECKSKIPFLAG=$(inigetkey "${UPDATECONFIG}" updatecontrol check_skip_update_flag)
	if test "${DOCHECKSKIPFLAG}" = "yes"; then
		DOSKIPUPDATE=$(inigetkey "${SYSCONFIG}" usbupdate skip_usb_update_flag)
		if test "${DOSKIPUPDATE}" = "1"; then
			inisetkey ${UPDATESTATE} lastboot denied_update_flag 1
		else
			inisetkey ${UPDATESTATE} lastboot denied_update_flag 0
		fi		
	else
		inisetkey ${UPDATESTATE} lastboot denied_update_flag 0
	fi

	ARTNUM=$(cat /proc/device-tree/fdt-artnum)
	SERNUM=$(inigetkey /tmp/.system/ubootenv.ini SERIAL SERIALNUMBER)
	inisetkey ${UPDATESTATE} lastboot serial_number "${ARTNUM}${SERNUM}"

	CURRDATE=$(date "+%Y-%m-%d %H:%M:%S")
	inisetkey ${UPDATESTATE} lastboot date "${CURRDATE}"
	
	return
}

#write_update_stat_usbupdate( error_flag, exit_code, exit_message )
write_update_stat_usbupdate()
{
	if test "$1"; then
		inisetkey ${UPDATESTATE} usbupdate error_flag "$1"
	fi
	
	if test "$2"; then
		inisetkey ${UPDATESTATE} usbupdate exit_code "$2"
	fi
	
	if test "$3"; then
		inisetkey ${UPDATESTATE} usbupdate exit_message "$3"
	fi

	ARTNUM=$(cat /proc/device-tree/fdt-artnum)
	SERNUM=$(inigetkey /tmp/.system/ubootenv.ini SERIAL SERIALNUMBER)
	inisetkey ${UPDATESTATE} usbupdate serial_number "${ARTNUM}${SERNUM}"

	CURRDATE=$(date "+%Y-%m-%d %H:%M:%S")
	inisetkey ${UPDATESTATE} usbupdate date "${CURRDATE}"
	
	return
}

check_run_fwupdate()
{
	local DOUPDATE
	local PACKAGENAME
	local NUMFILES
	local PACKAGEFILE
	
	if test -f "${SKIPFWUPDATEFLAGFILE}"; then
		printlog "Firmwareupdate previously run, skipping it this time ..."
		return
	fi
	
	DOUPDATE=$(inigetkey "${UPDATECONFIG}" firmware do_update)
	if test "${DOUPDATE}" != "yes"; then
		return
	fi
	
	# Find out the firmware file to be used
	PACKAGEFILE=$(inigetkey "${UPDATECONFIG}" firmware firmware_name)
	if test -z "${PACKAGEFILE}"; then
		# No package name given. Search for one. Valid if ONLY ONE is found.
		printlog "No package name given, will search for one ..."
		NUMFILES=$(find ${PACKAGEPATH}/firmware -name "firmware_mx6-*.tgz" -maxdepth 1 | wc -l)
		if test "${NUMFILES}" -eq 1; then
			PACKAGEFILE=$(find ${PACKAGEPATH}/firmware -name "firmware_mx6-*.tgz" -maxdepth 1)
		else
			write_update_stat_usbupdate "1" "1" "fwupdate: no firmware file given"
			exitWithError "No firmware file given and no file seems to be a firmware file in the directory!"
		fi
	else
		# expand name with path
		PACKAGEFILE=${PACKAGEPATH}/firmware/${PACKAGEFILE}
	fi
	
	PACKAGENAME=$(basename ${PACKAGEFILE})
	printlog "Will use package ${PACKAGENAME}"
	if test ! -f "${PACKAGEFILE}" ; then
		write_update_stat_usbupdate "1" "2" "fwupdate: firmware file not found"
		exitWithError "Firmware file <${PACKAGEFILE}> not found in firmware subdirectory!"
	fi
	
	# Now start update
	printlog "Starting firmwareupdate"
	syspackage "${PACKAGEFILE}" "${UPDATEMODE}" "${LOGFILE}"
	if test "$?" = "0"; then
		# Is it really possible to come to this point without an error in syspackage ??
		# In my opinion the call to syspackage should never return!
		write_update_stat_usbupdate "1" "3" "fwupdate: internal error"
		reboot
	fi
	write_update_stat_usbupdate "1" "4" "fwupdate: package update returned with error"
	exitWithError "Firmware package update returned with error!"
}

check_run_splashscreen()
{
	DOUPDATESPLASH=$(inigetkey "${UPDATECONFIG}" splashscreen do_update_splashscreen)
	if test "${DOUPDATESPLASH}" != "yes"; then
		# Nothing to do
		return
	fi
	printlog "Installing new splash screen"
	if test ! -f "${PACKAGEPATH}/splashscreen/splash.png"; then
		write_update_stat_usbupdate "1" "10" "splashupdate: file not found"
		exitWithError "New splash screen file <splash.png> not found in splashscreen subdirectory!"
	fi
	cp -f ${PACKAGEPATH}/splashscreen/splash.png /usr/local/etc
	syscfg -g all
}

check_run_webtheme()
{
	DOUPDATEWEBTHEME=$(inigetkey "${UPDATECONFIG}" webtheme do_update_webtheme)
	if test "${DOUPDATEWEBTHEME}" != "yes"; then
		# Nothing to do
		return
	fi
	
	printlog "Installing new web theme"
	if test ! -f "${PACKAGEPATH}/webtheme/logo.gif"; then
		write_update_stat_usbupdate "1" "20" "webtheme: file not found"
		exitWithError "New web theme file <logo.gif> not found in webtheme subdirectory!"
	fi
	
	cp -f ${PACKAGEPATH}/webtheme/logo.gif /usr/local/etc
	syscfg -g all
}

check_run_licenses()
{
	DOUPDATELICENSES=$(inigetkey "${UPDATECONFIG}" license do_update_licenses)
	if test "${DOUPDATELICENSES}" != "yes"; then
		return
	fi
	
	SYSSERNUM=$(printf "%05u" $(cat /sys/fsl_otp/HW_OCOTP_SRK0))
	SYSARTNUM=$(printf "%u" $(cat /sys/fsl_otp/HW_OCOTP_SRK2))
	
	if test ! -f "${PACKAGEPATH}/license/${SYSARTNUM}-${SYSSERNUM}.tgz"; then
		printlog "No license package for ${SYSARTNUM}-${SYSSERNUM} found in license subdirectory"
	else
		printlog "Installing license package for ${SYSARTNUM}-${SYSSERNUM}"
		syspackage "${PACKAGEPATH}/license/${SYSARTNUM}-${SYSSERNUM}.tgz" "${UPDATEMODE}" "${LOGFILE}"
		if test "$?" != "0"; then
			write_update_stat_usbupdate "1" "30" "License-Install: file not found"
			exitWithError "License package update returned with error!"
		fi
	fi
}

check_run_sysconfig()
{
	local SYSCONFIGININAME=configuration.ini
	local DORESETSYSCONFIG
	local DOSYSCONFIGFILE
	local REPLACECONFIG
	
	DORESETSYSCONFIG=$(inigetkey "${UPDATECONFIG}" sysconfig do_reset_syscfg_to_factory_defaults)
	if test "${DORESETSYSCONFIG}" = "yes"; then
		printlog "Resetting system configuration to factory defaults"
		syscfg -r
	fi
	
	REPLACECONFIG=$(inigetkey "${UPDATECONFIG}" sysconfig replace_config_file_instead_of_merge)
	if test "${REPLACECONFIG}" = "yes"; then
		cp -f ${PACKAGEPATH}/sysconfig/${SYSCONFIGININAME} /usr/local/etc/syscfg.ini
	fi
	
	DOSYSCONFIGFILE=$(inigetkey "${UPDATECONFIG}" sysconfig do_sysconfig_from_file)
	if test "${DOSYSCONFIGFILE}" = "yes"; then
		
		if test ! -f "${PACKAGEPATH}/sysconfig/${SYSCONFIGININAME}"; then
			write_update_stat_usbupdate "1" "40" "sysconfig: file not found"
			exitWithError "System configuration file <${SYSCONFIGININAME}> not found in sysconfig subdirectory!"
		fi
		printlog "Updating system configuration"
		syscfg -i "${PACKAGEPATH}/sysconfig/${SYSCONFIGININAME}" -g all
	fi
}

check_run_plcapp()
{
	DOUPDATEPLCAPP=$(inigetkey "${UPDATECONFIG}" plcapp do_update_plcapp)
	if test "${DOUPDATEPLCAPP}" != "yes"; then
		return
	fi
	
	if test "${DEVICETYPE}" != "plc"; then
		write_update_stat_usbupdate "1" "50" "plcapp: wrong devicetype"
		exitWithError "Updating the plc application is not supported for devices of type ${DEVICETYPE}"
	fi
	
	PLCAPPNAME=$(inigetkey "${UPDATECONFIG}" plcapp plcapp_name)
	if test ! -f "${PACKAGEPATH}/application/${PLCAPPNAME}"; then
		write_update_stat_usbupdate "1" "51" "plcapp: file not found"
		exitWithError "PLC application package ${PLCAPPNAME} not found in application subdirectory"
	fi
	printlog "Installing PLC application package ${PLCAPPNAME}"
	syspackage "${PACKAGEPATH}/application/${PLCAPPNAME}" "${UPDATEMODE}" "${LOGFILE}"
	if test "$?" != "0"; then
		write_update_stat_usbupdate "1" "52" "plcapp: package update returned with error"
		exitWithError "PLC package update returned with error!"
	fi
}

check_run_cleanplcfolder()
{
	DOCLEANPLCFOLDER=$(inigetkey "${UPDATECONFIG}" plcapp do_clean_plcfolder)
	if test "${DOCLEANPLCFOLDER}" != "yes"; then
		return
	fi

	if test "${DEVICETYPE}" != "plc"; then
		write_update_stat_usbupdate "1" "60" "cleanfolder: wrong devicetype"
		exitWithError "Cleaning the plc folder is not supported for devices of type ${DEVICETYPE}"
	fi

	printlog "Cleaning PLC folder. Removing all files in /home/plc/applications."
	compare_fw_version "1" "1" "0"
	if test  $? -gt 0; then
		printlog "System firmware is higher or equal to 1.1.0: using plcctrl tool"
		plcctrl -cf
		if test "$?" != "0"; then
			write_update_stat_usbupdate "1" "61" "cleanfolder: cleaning failed"
			exitWithError "Clearing PLC folder failed"
		fi
	else
		printlog "System Firmware is lower than 1.1.0: using plc_clean script"
		(
			# create subshell to preserve working dir of install script
			cd /home/plc/applications
			# Achtung, geht rekursiv in die Verzeichnisstruktur
			find . ! -name "." ! -name ".." ! -name "CODESYSControl.cfg" ! -name "visu" -exec rm -rf {} \; 2>/dev/null
			cd -
		)
	fi
	printlog "PLC folder successfully cleared"
}

check_run_copy_plcdata()
{
	DOCOPYPLCDATA=$(inigetkey "${UPDATECONFIG}" plcapp do_copy_plcdata)
	if test "${DOCOPYPLCDATA}" != "yes"; then
		return
	fi
	printlog "Copy all files from application/data to /home/plc/applications"
	if test ! -d "${PACKAGEPATH}/application/data"; then
		printlog "No directory application/data available"
		return
	fi
	if test "$(ls -1 ${PACKAGEPATH}/application/data)"; then
		cp -fr "${PACKAGEPATH}/application/data/"* /home/plc/applications
	else
		printlog "No files found in application/data"
	fi
}

check_run_filecopy()
{
	DOFILECOPY=$(inigetkey "${UPDATECONFIG}" filecopy do_filecopy)
	if test "${DOFILECOPY}" != "yes"; then
		return
	fi
	
	# extract filecopy script from package
	tar xzf "${PACKAGEFILE}" filecopy -C ./
	if test $? -ne 0 ; then
		write_update_stat_usbupdate "1" "70" "filecopy: extracting copy script failed"
		exitWithError "Extracting filecopy script from package failed!"
	fi

	printlog "Doing userdefined file actions"
	dos2unix -u ./filecopy
	chmod +x filecopy || exit 1
	./filecopy "${PACKAGEPATH}" "${LOGFILE}"
}

check_run_customscript()
{
	DOCUSTOMSCRIPT=$(inigetkey "${UPDATECONFIG}" customscript do_customscript)
	if test "${DOCUSTOMSCRIPT}" != "yes"; then
		return
	fi
	
	CUSTOMSCRIPTNAME=$(inigetkey "${UPDATECONFIG}" customscript customscript_name)
	if test -z "${CUSTOMSCRIPTNAME}"; then
		write_update_stat_usbupdate "1" "80" "customscript: file not found"
		exitWithError "No script name for custom script defined!"
	fi
	
	if test ! -f "${PACKAGEPATH}/customscript/${CUSTOMSCRIPTNAME}"; then
		write_update_stat_usbupdate "1" "81" "customscript: file not found"
		exitWithError "No script <${CUSTOMSCRIPTNAME}> found in customscript subdirectory"
	fi
	
	printlog "Executing custom script ${CUSTOMSCRIPTNAME}"
	dos2unix -u "${PACKAGEPATH}/customscript/${CUSTOMSCRIPTNAME}"
	chmod +x "${PACKAGEPATH}/customscript/${CUSTOMSCRIPTNAME}"
	(
		# use subshell - this way we can change the working directory
		# without influencing the rest of this script
		cd /tmp
		${PACKAGEPATH}/customscript/${CUSTOMSCRIPTNAME}
		exit $?
	)
	if test $? -ne 0 ; then
		write_update_stat_usbupdate "1" "82" "customscript: returned with error"
		exitWithError "Custom script failed with error!"
	fi
}

check_run_cleanfontsfolder()
{
        DOCLEANFONTSFOLDER=$(inigetkey "${UPDATECONFIG}" fonts do_clean_fontsfolder)
        if test "${DOCLEANFONTSFOLDER}" != "yes"; then
                return
        fi

        if test "${DEVICETYPE}" != "plc"; then
                printlog "Cleaning ET/WT fonts folder. Removing all files in /usr/share/fonts/X11/userfonts."
                /bin/mount -o remount,rw /usr
        else
                printlog "Cleaning PLC fonts folder. Removing all files in /home/plc/applications/fonts."
        fi

        cd ${FONTDESTINATION}
        rm -f * 2>/dev/null
        cd -

        if test "${DEVICETYPE}" != "plc"; then
                /bin/mount -o remount,ro /usr
        fi

        printlog "Fonts folder successfully cleared"
}

check_run_fonts()
{
        DOUPDATEFONTS=$(inigetkey "${UPDATECONFIG}" fonts do_update_fonts)
        if test "${DOUPDATEFONTS}" != "yes"; then
                # Nothing to do
                return
        fi

        printlog "Installing new fonts"
        if ! test "$(ls -A "${PACKAGEPATH}/fonts")"; then
                write_update_stat_usbupdate "1" "90" "fonts: no files found"
                exitWithError "No files found in fonts subdirectory!"
        fi

        if test "${DEVICETYPE}" != "plc"; then
                /bin/mount -o remount,rw /usr
        fi

        if ! test -d "${FONTDESTINATION}"; then
                printlog "Fonts folder does not exist, creating"
                /bin/mkdir ${FONTDESTINATION}
                printlog "Copying fonts to destination"
        fi

        cp -f ${PACKAGEPATH}/fonts/*.ttf ${FONTDESTINATION}
        cp -f ${PACKAGEPATH}/fonts/*.TTF ${FONTDESTINATION}
        chmod a-x ${FONTDESTINATION}/*

        if test "${DEVICETYPE}" != "plc"; then
                /usr/bin/mkfontscale ${FONTDESTINATION}
                /usr/bin/mkfontscale -b -s -l -- ${FONTDESTINATION}
                /bin/mount -o remount,ro /usr
        fi
}

check_run_reboot()
{
	DOFINALREBOOT=$(inigetkey "${UPDATECONFIG}" updatecontrol do_finalreboot)
	if test -z "${DOFINALREBOOT}"; then
		# compatibility
		DOFINALREBOOT=$(inigetkey "${UPDATECONFIG}" reboot do_finalreboot)
	fi

	if test "${DOFINALREBOOT}" = "yes"; then
		printlog "Final reboot requested ... system will reboot now"
		echo "[usbupdate]" > /tmp/usbupdate.ini
		echo "deny_usb_update_on_next_boot=\"yes\"" >> /tmp/usbupdate.ini
		syscfg -i /tmp/usbupdate.ini -g usbupdate
		rm /tmp/usbupdate.ini
		write_update_stat_usbupdate "0" "0" "Package installation finished - executing final reboot"
		reboot
	fi
}

check_run_autostart()
{
	DOAUTOSTARTDELETE=$(inigetkey "${UPDATECONFIG}" autostart do_delete_autostart)
	if test "${DOAUTOSTARTDELETE}" = "yes"; then
		printlog "Deleting autostart scripts"
		rm -f /home/app/autostart
		rm -f /home/app/autostart_sync
	fi
	
	DOAUTOSTARTINSTALL=$(inigetkey "${UPDATECONFIG}" autostart do_install_autostart)
	if test "${DOAUTOSTARTINSTALL}" = "yes"; then
		if test -f "${PACKAGEPATH}/autostart/autostart"; then
			cp -f ${PACKAGEPATH}/autostart/autostart /home/app/
			dos2unix -u /home/app/autostart
			chmod +x /home/app/autostart
			printlog "Installing 'autostart' script"
		else
			printlog "No 'autostart' script found"
		fi

		if test -f "${PACKAGEPATH}/autostart/autostart_sync"; then
			cp -f ${PACKAGEPATH}/autostart/autostart_sync /home/app/
			dos2unix -u /home/app/autostart_sync
			chmod +x /home/app/autostart_sync
			printlog "Installing 'autostart_sync' script"
		else
			printlog "No 'autostart_sync' script found"
		fi
	fi
}

check_skip_update()
{
	DOCHECKSKIPFLAG=$(inigetkey "${UPDATECONFIG}" updatecontrol check_skip_update_flag)
	if test "${DOCHECKSKIPFLAG}" = "yes"; then
		DOSKIPUPDATE=$(inigetkey "${SYSCONFIG}" usbupdate skip_usb_update_flag)
		if test "${DOSKIPUPDATE}" = "1"; then
			exitOk "Skipping USBUpdate due to skip_usb_update_flag in system configuration"
		fi	
	fi
	
	return
}

check_run_test()
{
	DOTEST=$(inigetkey "${UPDATECONFIG} test do_test")
	if test "${DOTEST}" != "yes"; then
		return
	fi
	
	printlog "Testmode activated"
	TESTDOFAIL=$(inigetkey "${UPDATECONFIG}" test test_do_fail)
	TESTEXITMESSAGE=$(inigetkey "${UPDATECONFIG}" test test_exit_message)
	if test "${TESTDOFAIL}" == "no"; then
		exitOk "${TESTEXITMESSAGE}"
	else
		exitWithError "${TESTEXITMESSAGE}"
	fi
}

### MAIN ###

printlog "USBUpdate Package Version ${USBUPDATE_VERSION} running"

# USB Update ablauf
if test ! -e "${UPDATECONFIG}"; then
	exitWithError "Update configuration file usbupdate-mx6/usbupdate.ini is missing on usb drive!"
fi

# convert dos line endings
dos2unix -u "${UPDATECONFIG}"

check_run_test
write_update_stat_lastboot

check_skip_update
write_update_stat_usbupdate "0" "0" "---"

check_run_fwupdate
check_run_autostart
check_run_webtheme
check_run_splashscreen
check_run_licenses
check_run_sysconfig
check_run_cleanplcfolder
check_run_plcapp
check_run_copy_plcdata
check_run_cleanfontsfolder
check_run_fonts
check_run_filecopy
check_run_customscript
check_run_reboot

write_update_stat_usbupdate "0" "0" "Package installation finished"
exitOk "Package installation finished successfully!"
