#!/bin/sh

export PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
UPDATE_TARBALL=update.tar.gz

echo "Update Script started"
pre_vers=`head -n 1 /etc/cabversion`

path=$PWD

# Working directory for passwd, group, and shadow merges
pwdupdir="/tmp/pwdupdir"
mkdir -p ${pwdupdir}
#<PASSWD>
#<GROUP>
#<SHADOW>

# Save password files before extracting the TAR archive
for f in passwd group shadow; do
	cp "/etc/${f}" "${pwdupdir}/${f}.bkp"
done

cd /
## tar will fail immediately if a directory is about to be replaced
## by a file or a symlink. Thus, check here for this problem and
## remove directories before unpacking the tar file.
tar tf ${path}/${UPDATE_TARBALL} | awk '
	/[^\/]$/ {
		if( system("test -d \"" $0 "\" -a ! -h \"" $0 "\"") == 0 ) {
			print("  Replacing directory " $0 " with file or symlink");
			system("rm -rf \"" $0 "\"" );
		}
	}
'
tar xzf ${path}/${UPDATE_TARBALL}
sync

## Merge password files if they have changed.
## Therefore, determine which entries are new
## or have changed and apply them to the new
## file from the update.
## A changed entry will be dropped if the entry
## is removed by the update.
for file in passwd group shadow; do
	# Variables
	fnew="/etc/${file}"
	fbkp="${pwdupdir}/${file}.bkp"
	fold="${pwdupdir}/${file}"
	fdiff="/tmp/${file}.diff"
	mode="$(stat -c '%a' ${fnew})"

	# Skip if file has not changed
	diff -q ${fnew} ${fbkp} &>/dev/null && continue

	# Removing all default lines from the old and the
	# new file. The result is changed or new entries.
	grep -v -x -F -f ${fnew} -f ${fold} ${fbkp} > ${fdiff}

	# Create a temporary file for the modifications
	tmp="$(mktemp ${pwdupdir}/${f}-XXXXXX)"
	# Apply the changes to the new file
	awk -F: '
	function pr(type, msg) {
		printf( "  %-7s %7s %s\n", "'${file}':", type, msg) > "/dev/stderr";
	}
	function FileToMap(file, map,    a, cmd, line, n) {
		# Get entries from file and store them in an array
		# using the first token until ":" as key.
		while ( (getline line < file) > 0 ) {
			n = split(line, a, ":");
			if ( n < 2 )
				continue;
			map[a[1]] = line;
		}
		close(file);
	}
	function append(a, b) {
		if ( a != "" )
			a = a ", ";

		return a b;
	}
	BEGIN {
		# Store file entries in arrays
		# Note that awk passes arrays as reference
		FileToMap("'${fdiff}'", dMap);
		FileToMap("'${fold}'", oMap);
		FileToMap("'${fbkp}'", bMap);
	}
	/^[^:]+:/ {
		# Check if this value has been changed
		if ( $1 in dMap ) {
			# Yes, use changed value
			print dMap[$1];
			delete dMap[$1];
			msg["MERGED"] = append(msg["MERGED"], $1);
		} else {
			# No, keep the value
			if ( !($1 in oMap) && !($1 in bMap) ) {
				# Was neither in old nor in bkp file, so
				# it must be a new value
				msg["NEW"] = append(msg["NEW"], $1);
			}
			print $0;
		}
		next;
	}
	{
		# Other lines, keep them as they are
		print $0;
		next;
	}
	END {
		# Loop over remaining changed values
		for ( i in dMap ) {
			if ( !(i in oMap) ) {
				# Was also not in the old file,
				# so keep this extra entry.
				print dMap[i];
				msg["EXTRA"] = append(msg["EXTRA"], i);
			} else {
				# Used to be in the old file, but
				# is not anymore in the new file.
				# Must be deleted by the update,
				# thus ignore this entry.
				msg["DROPPED"] = append(msg["DROPPED"], i);
			}
		}

		# Print results
		for ( i in msg )
			pr(i, msg[i]);

	}
	' ${fnew} 2>&1 >${tmp}
	if [ $? -ne 0 ]; then
		echo "${file}:\tMERGE FAILED, CHECK PASSWORDS AND GROUPS!!!"
		continue
	fi

	# Update new file but keep the previous file as backup
	mv ${fbkp} ${fnew}-
	mv ${tmp} ${fnew}
	# ${tmp} had mode 600, so change it correctly
	chmod ${mode} ${fnew} ${fnew}-
done

# Clean-up
rm -rf ${pwdupdir}
sync

#<REMOVE>
rm -f /etc/factory/scripts.d/lteinfo.sh
rm -f /usr/lib/libexpat.so.1.8.10
sync

# Unpack tar file again.
# If directories are replaced by a symlink,
# the removes above might delete files.
# Restore them here again.
tar xzf ${path}/${UPDATE_TARBALL}
sync

cd ${path}

## Update Kernel if present
if [ -r /root/apf6-linux.bin ]; then
	echo "Installing new kernel"
	mount -t ext4 /dev/mmcblk2p1 /mnt

	mv /root/apf6-linux.bin /mnt/

	sync
	umount /mnt
fi

## Update Device Trees if present
if ls /root/imx6*.dtb &> /dev/null; then
	echo "Installing new Device Trees"
	mount -t ext4 /dev/mmcblk2p1 /mnt

	for i in /root/imx6*.dtb; do
		if [ -r "$i" ]; then
			echo "  * $(basename "$i")"
			mv $i /mnt/
		fi
	done
	sync
	umount /mnt
fi

## Update Bootloader (u-boot) if present
if [ -r /root/apf6-u-boot.img -a -r /root/apf6-u-boot.spl ]; then
	echo "Installing new bootloader"

	echo 0 > /sys/block/mmcblk2boot0/force_ro

	# Flash SPL
	dd if=/root/apf6-u-boot.spl of=/dev/mmcblk2boot0 bs=512 seek=2 status=none
	# Flash bootloader
	dd if=/root/apf6-u-boot.img of=/dev/mmcblk2boot0 bs=512 seek=138 status=none
	# Erase environment and backup environment
	dd if=/dev/zero of=/dev/mmcblk2boot0 bs=512 seek=2048 count=2048 status=none

	echo 1 > /sys/block/mmcblk2boot0/force_ro

	rm /root/apf6-u-boot.*

	sync
fi

ldconfig

echo "Update finished."

post_vers=`head -n 1 /etc/cabversion`
echo "Updated from ${pre_vers} to ${post_vers}"

echo "The config file /etc/cablynx.conf was untouched."
echo "Please use the new config file in /etc/conf.d/cablynx.factory"

