#!/bin/sh

prog=${0##*/}
version=2.3-7-g07ea387

cleanup() {
	if [ -n "$uninstalls" ]; then
		apk del -q syslinux
	fi
	if [ -n "$umounts" ]; then
		umount $umounts
	fi
}

die() {
	echo "$@" >&2
	cleanup
	exit 1
}

# find device for mountpoint
find_dev() {
	local mnt="${1%/}" # strip trailing /
	awk "\$2 == \"$mnt\" {print \$1}" /proc/mounts
}

# check if given device is on usb bus
on_usb_bus() {
	local dev="$1"
	[ -e /sys/block/$dev ] || return 1
	local sysdev=$(readlink -f /sys/block/$dev/device)
	test "${sysdev##*/usb[0-9]}" != "$sysdev"
}


usage() {
	cat <<__EOF__
$prog $version
usage: $prog [-hu] SOURCE [DEST]

Copy the contents of SOURCE to DEST and make DEST bootable.

SOURCE can be a directory or a ISO image. DEST can be a mounted directory 
or a device. If DEST is ommitted /media/usb will be used.

Options:
 -f  Force overwrite existing files. Will overwrite syslinux.cfg if upgrade.
 -h  Show this help.
 -u  Upgrade mode. Keep existing syslinux.cfg and don't run syslinux.
 -s  Force run syslinux, even if upgrade mode.
 -v  Verbose mode. Display whats going on.

__EOF__
	exit 1
}

while getopts "fhusv" opt; do
	case "$opt" in
	f) force=1;;
	h) usage;;
	u) upgrade=1;;
	s) syslinux=1;;
	v) verbose=1;;
	esac
done

shift $(($OPTIND - 1))

src=${1}
dest=${2:-/media/usb}

srcmnt=${MNT:-/mnt}

[ -z "$src" ] && usage

# Find the srcdir, mount loopback if needed
if [ -f "$src"/.alpine-release ]; then
	srcdir="$src"
else
	mount -o loop -t iso9660 "$src" $srcmnt \
		|| die "Failed to mount loopback $src"
	umounts="$srcmnt"
	srcdir="$srcmnt"
	[ -f "$srcdir"/.alpine-release ] || die "No .alpine-release found on image $src"
fi
to_version=$(cat "$srcdir"/.alpine-release)

# find target device
if [ -d "$dest" ]; then
	dest=${dest%/} # strip trailing /
	if ! awk '{print $2}' /proc/mounts | grep -q "^$dest\$"; then
		mount "$dest" || die "Failed to mount $dest"
		umounts="$umounts $dest"
	fi
	destdir="$dest"
	dest=$(find_dev "$destdir")
elif [ -b "$dest" ]; then
	destdir="/media/${dest##*/}"
	mkdir -p "$destdir"
	mount "$dest" "$destdir" || die "Failed to mount $dest on $destdir"
	umounts="$umounts $destdir"
fi
[ -n "$verbose" ] && echo "Using $dest as target (mounted on $destdir)"


# find parent device (i.e sda)
dev="$dest"
while [ -L "$dev" ]; do
	dev=$(readlink -f $dev)
done
parent_dev=/dev/$(basename $(dirname /sys/block/*/$(basename $dev)))

# check if this files exist and not in upgrade mode
if [ -z "$upgrade" ] && [ -z "$force" ]; then
	for i in boot apks syslinux.cfg .alpine-release; do
		[ -e "$destdir"/$i ] && die "$destdir/$i already exists. Aborting"
	done
fi

# check if its same version
if [ -n "$upgrade" ] && [ -e "$destdir"/.alpine-release ]; then
	from_version=$(cat "$destdir"/.alpine-release)
	if [ -z "$force" ] && [ "$from_version" = "$to_version" ]; then
		die "Source and target seems to have same version ($from_version). Aborting."
	fi
fi

# Display what versions we are installing/upgrading
if [ -n "$from_version" ]; then
	echo "Upgrading $dest from $from_version to $to_version"
else
	echo "Copying $to_version to $dest (mounted on $destdir)"
fi

# remove partial upgrades if any.
rm -rf "$destdir"/.new "$destdir"/.old

# check that we have the space we need
needed_space=$(cd "$srcdir" && du -s -c boot apks syslinux.cfg .alpine-release | awk '$2 == "total" {print $1}')
[ -n "$verbose" ] && echo "Needed space:    $needed_space kB"

available_space=$(df -k "$destdir" | tail -n 1 | awk '{print $4}')
[ -n "$verbose" ] && echo "Available space: $available_space kB"
[ $available_space -lt $needed_space ] && die "Not enough space on $destdir. Aborting."

# copy the files to .new
mkdir -p "$destdir"/.new || die "Failed to create $destdir/.new"
for i in boot apks syslinux.cfg .alpine-release; do
	[ -n "$verbose" ] && echo "Copying $srcdir/$i to $destdir/.new/"
	cp -a "$srcdir"/$i "$destdir"/.new/
done

# make sure files are really there before we replace existing
[ -n "$verbose" ] && echo "Flushing cache..."
sync

[ -n "$verbose" ] && echo "Replacing existing files..."
mkdir -p "$destdir"/.old || die "Failed to create $destdir/.old"

# do we want keep existing syslinux.cfg?
tomove="boot apks .alpine-release"
if [ -n "$force" ] || ! [ -e "$destdir"/syslinux.cfg ]; then
	tomove="$tomove syslinux.cfg"
	# update syslinux.cfg unless device is on usb bus
	# this is so we can boot from CF's and harddisk 
	if ! on_usb_bus $parent_dev; then
		[ -n "$verbose" ] && echo "Updating syslinux.cfg to use $dest"
		sed -i -e "s/usbdisk/${dest##*/}/g" \
			"$destdir"/.new/syslinux.cfg
	fi
fi

# move current files to .old
for i in $tomove; do
	if [ -e "$destdir"/$i ]; then
		mv "$destdir"/$i "$destdir"/.old/ || die "Failed to move $destdir/$i to $destdir/.old/"
	fi
done

# move .new to current
for i in $tomove; do
	mv "$destdir"/.new/$i "$destdir"/ || die "Failed to move $destdir/.new/ to $destdir"
done

# cleanup
[ -z "$keep_old" ] && rm -rf "$destdir"/.old "$destdir"/.new
sync


# If we only copy then we are done.
if [ -n "$upgrade" ] && [ -z "$syslinux" ]; then
	cleanup
	exit 0
fi

echo "Making $dest bootable..."

if ! [ -x "$(which syslinux)" ]; then
	apk add -q syslinux || die "Failed to install syslinux"
	uninstalls="syslinux"
fi

syslinux $dest

if [ -b $parent_dev ]; then
	dd if=/usr/share/syslinux/mbr.bin of=$parent_dev
else
	echo "Warning: Could not find the parent device for $dest"
fi

cleanup
sync
