#!/bin/bash
# Build FatdogArm/Fatdog64 basesfs
#
# Copyright (C) James Budiono, 2013, 2014
# License: GPL Version 3 or later
# 
# $1-build option
#    download - download only
#    install - download and install only
#    sfs - download, install and make sfs
#    sfs-pause - same as sfs, but pause after install and before creating sfs
#    (no parameter) - same as "sfs"
#
# $2-config file (default: ./build.conf)
#
# Version 2: Re-write.
# Version 3: Lots of improvements.
#
# Note: must run as root. This script is multiplatform (Fatdog64, FatdogArm)

export LANG=C # make it run a bit faster

### customisable configuration items
PKG_EXT=${PKG_EXT:-txz} # other choice is tgz or txz
PKG_URL=${PKG_URL:-http://distro.ibiblio.org/fatdog/packages/700}
PKG_CACHE=${PKG_CACHE:-/tmp/pkgcache}
PKG_LIST=${PKG_LIST:-sfs-pkglist}
GEN_EXCLUSION_LIST=${GEN_EXCLUSION_LIST-./gen-exclusion-list}

#SQUASHFS_PARAM= # inherit # -comp xz -Xbcj x86
SFS_OUTPUT=${SFS_OUTPUT:-fd64.sfs}
#NOPAD=1 # don't pad SFS output
OUTPUT_DIR=${OUTPUT_DIR:-/tmp/output}
KEEP_ROOTFS=${KEEP_ROOTFS:-$ROOTFS_DIR} # if rootfs_dir is specified, keep it (don't delete on exit)
ROOTFS_DIR=${ROOTFS_DIR:-/tmp/rootfs.$$}
INSTALLER_PKG=${INSTALLER_PKG:-pkgtools-14.0-noarch-1.txz}

BUILD_CONFIG=${2:-$BUILD_CONFIG}
BUILD_CONFIG=${BUILD_CONFIG:-./build.conf}

### standard configuration items - dont change unless you know what you're doing
[ -e "$BUILD_CONFIG" ] && . "$BUILD_CONFIG"
CHECKSUM_FILE=CHECKSUMS.md5
#INSTALLER="$ROOTFS_DIR"/sbin/installpkg
#INSTALLER=$(readlink -f ./scripts/ipkg) # internal installer
INSTALLER=installpkg

### run-time variables
NUMPKGS=0



################ helpers ###################

# download: $1-file (no path)
download_file() {
	echo downloading "$1" ...
	! [ "${1%/*}" = "${1##*/}" ] && mkdir -p "$PKG_CACHE/${1##*/}" # create path
	wget -c -o /dev/null -O "$PKG_CACHE/$1" "$PKG_URL/$1" 
}

# check file integrity: $1-file (no path)
check_md5() {
	echo -n "Checking integrity $1 ... "
	read goodsum < <(awk -v pkg="./$1" '$2==pkg {print $1;exit} END {print "NONE"}' "$PKG_CACHE/$CHECKSUM_FILE")
	read filesum < <(md5sum "$PKG_CACHE"/$1 | awk '{print $1}')
	if [ $goodsum = "NONE" ]; then
		echo "UNLISTED !"
		return 0
	elif [ $goodsum != $filesum ]; then
		echo "BAD CHECKSUM !"
		return 1
	fi
	echo good.
	return 0
}



################ action handlers ###############

### cleanup
trap 'cleanup; exit' INT QUIT TERM 0
cleanup() {
	[ -z "$KEEP_ROOTFS" ] && rm -rf "$ROOTFS_DIR"
}

###
setup() {
	# 1. important files must exist
	if ! [ -e "$PKG_LIST" ]; then
		echo Missing components.
		exit
	fi

	# 2. make dirs and get checksum
	mkdir -p "$PKG_CACHE" "$ROOTFS_DIR" "$OUTPUT_DIR"
	! [ -s "$PKG_CACHE/$CHECKSUM_FILE" ] && download_file $CHECKSUM_FILE
	! [ -s "$PKG_CACHE/$CHECKSUM_FILE" ] && echo "Failed to download $CHECKSUM_FILE" && exit
	
	# 3. count number of packages to process
	while read p; do
		case "$p" in
			\#*|"") continue ;;
			*) NUMPKGS=$((NUMPKGS + 1)) ;;
		esac
	done < "$PKG_LIST"
}

###
download() {
	echo downloading packages ...
	PKGS=0
	while read pkg; do
		pkg=${pkg%%#*}; set -- $pkg; pkg=$1
		[ -z $pkg ] && continue;
		PKGS=$((PKGS+1))
		echo -n "$PKGS of $NUMPKGS: "
		if ! [ -s "$PKG_CACHE/$pkg.${PKG_EXT}" ]; then
			download_file $pkg.${PKG_EXT}
		else 
			echo $pkg already exist, not downloaded.
		fi
		if ! check_md5 $pkg.${PKG_EXT}; then
			# bad checksum, attempt re-download
			rm -f "$PKG_CACHE/$pkg.${PKG_EXT}"
			echo Attempting re-download: $pkg
			download_file $pkg.${PKG_EXT}
			check_md5 $pkg.${PKG_EXT}
		fi
	done < "$PKG_LIST"
}

###
installer() {
	echo preparing installer ...
	export ROOT="$ROOTFS_DIR"
	return # use internal installer now
	
	! [ -e "$PKG_CACHE/$INSTALLER_PKG" ] && download_file $INSTALLER_PKG 
	! check_md5 $INSTALLER_PKG && echo "Bad installer package." && exit
	
	tar -xf "$PKG_CACHE"/$INSTALLER_PKG -C "$ROOTFS_DIR"
	rm -rf "$ROOTFS_DIR"/install
	if ! [ -e $INSTALLER ]; then
		echo "Can't prepare installer, exiting."
		exit
	fi
}

###
install() {
	# 1. packages
	echo installing packages ...
	PKGS=0
	while read pkg; do
		pkg=${pkg%%#*}; set -- $pkg; pkg=$1
		[ -z $pkg ] && continue
		PKGS=$((PKGS+1))
		
		# don't install if already installed previously
		[ -e "$ROOTFS_DIR/var/log/packages/$pkg" ] && 
		echo $PKGS of $NUMPKGS: "$pkg already installed, skipped..." && continue
		
		echo $PKGS of $NUMPKGS: installing "$pkg" ...
		$INSTALLER "$PKG_CACHE/$pkg.${PKG_EXT}" > /dev/null 2>&1
	done < "$PKG_LIST"
		
	# 2. mark placeholders for exclusion
	#{
	#echo cleaning up...
	#pushd "$ROOTFS_DIR" > /dev/null
	#last-minute clean-up
	#popd > /dev/null
	#} 2>/dev/null	
}

###
makesfs() {
	echo generating exclusion list ...
	echo "#" > "$ROOTFS_DIR"/sfs-exclude
	[ "$GEN_EXCLUSION_LIST" ] && [ -x $GEN_EXCLUSION_LIST ] && 
	$GEN_EXCLUSION_LIST > "$ROOTFS_DIR"/sfs-exclude
	echo building sfs ...
	get_fossil_stamp > "$ROOTFS_DIR"/etc/fatdog-fossil-version
	mksquashfs "$ROOTFS_DIR" "$OUTPUT_DIR/$SFS_OUTPUT" -wildcards -noappend \
	            -ef "$ROOTFS_DIR"/sfs-exclude \
	            -e sfs-exclude $SQUASHFS_PARAM > /dev/null
	[ -z $NOPAD ] && padsfs "$OUTPUT_DIR/$SFS_OUTPUT"
}
get_fossil_stamp() {
	which fossil > /dev/null && fossil info > /dev/null && fossil info | awk '/checkout/ {print $2}' 
}

### align sfs to 256K so they are usable by qemu directly.
padsfs() {
	ORIGSIZE=$(stat -c %s "$1")
	BLOCKS256K=$(( ((ORIGSIZE/1024/256)+1) ))
	dd if=/dev/zero of="$1" bs=256K seek=$BLOCKS256K count=0
}

pause() {
	echo "Press Enter to continue ..."
	read
}

### check that all required packages are available w/o downloading
check() {
	echo check availability of local packages ...
	PKGS=0
	while read pkg; do
		pkg=${pkg%%#*}; set -- $pkg; pkg=$1
		[ -z $pkg ] && continue;
		PKGS=$((PKGS+1))
		echo -n "$PKGS of $NUMPKGS: "
		if ! [ -s "$PKG_CACHE/$pkg.${PKG_EXT}" ]; then
			echo MISSING $pkg
			continue
		fi
		if ! check_md5 $pkg.${PKG_EXT}; then
			# bad checksum, attempt re-download
			echo BAD DOWNLOAD $pkg
		fi
	done < "$PKG_LIST"
}

#################### main ######################

# $1-action
case "$1" in
	download)  # download only
		actions="download" ;;
		
	install)   # download and build rootfs
		KEEP_ROOTFS=yes
		actions="download installer install" ;;
		
	sfs|"")    # download, build rootfs, build sfs (=default action)
		actions="download installer install makesfs" ;;
		
	sfs-pause) # same as "sfs" but pause first before building
		actions="download installer install makesfs"
		[ -t 0 ] && actions="download installer install pause makesfs" ;;

	check)
		actions="check" ;;

	help|--help|-h)
		echo "Usage: ${0##*/} [download|install|sfs|sfs-pause|check]"
		exit
		;;
esac

# 2. always do setup
setup

# 3. run the actions
for action in $actions; do 
	$action
done

# 4. cleanup - executed by trap handler
