#!/bin/sh
#

set -ue
#set -x

#################### some settings ######################

# the base directory of your local development files
ROOT_DIR=$(cd $(dirname "$0"); pwd)

# the user may define a different location for the local configuration by setting "LOCAL_CONF_DIR"
# otherwise use the default (local.conf.d)
LOCALCONF_DIR=${LOCALCONF_DIR:-${ROOT_DIR}/local.conf.d}

MASTER=$LOCALCONF_DIR/master.img
BUILD_DIR=${BUILD_DIR:-$ROOT_DIR/build}
IMAGE_DIR=${IMAGE_DIR:-$BUILD_DIR/images}
SCRIPTS_DIR=$LOCALCONF_DIR/chroot_scripts.d
TEMPLATE_DIR=$LOCALCONF_DIR/template-tree.d
PKG_INSTALL_LIST_DIR=$LOCALCONF_DIR/package-install.lst.d
PKG_REMOVE_LIST_DIR=$LOCALCONF_DIR/package-remove.lst.d

ROOT_DIR=$BUILD_DIR/rootfs
BOOT_DIR=$BUILD_DIR/bootfs
TMP_DIR=$BUILD_DIR/tmp
BACKUP_DIR=$BUILD_DIR/backup

IMAGE_FILE=$IMAGE_DIR/remastered.iso
IMAGEZ_FILE=$IMAGE_DIR/remastered.iso.z

CDWRITER=0,0,0

################# quite constant things #################
# the relative name of the image file on the cd
KNOPPIX_IMAGE=KNOPPIX/KNOPPIX

# isofs options
MKISOFS_OPTIONS="-allow-multidot -U -D -iso-level 3 -b lib/grub/i386-pc/stage2_eltorito -no-emul-boot -boot-load-size 1 -boot-info-table -pad -R"


################### general functions ###################

# maybe this could be useful later
# for now it DOES NOT WORK
#
# get the path of a configuration file - local configuration files
# supersede default files
# parameter: base name of the configuration file
function get_config_file()
{
	[ -e "$LOCALCONF_DIR/$1" ] && echo "$LOCALCONF_DIR/$1" && return 0
	[ -e "$DEFAULTCONF_DIR/$1" ] && echo "$DEFAULTCONF_DIR/$1" && return 0
	echo "configuration file ($1) not found!" >&2
	exit 1
}


# just adds a tabulator at the beginning of every line (nice for sub-processes)
# you should redirect stderr to stdout before, by adding '2>&1' to the previous command
# example: "do_something_useful 2>&1 | indentation"
function indentation()
{
	sed 's/^/\t/'
}


function error_die()
{
	echo "$2" >&2
	exit $1
}

##################### active functions #################

function master_get()
{
	umount_procfs
	local MASTER_MNT=$TMP_DIR/mnt-master-iso
	local ROOT_MNT=$TMP_DIR/mnt-roof-cloop
	
	echo "Mounting the master's image ..."
	local MNT_OPTIONS=
	# use loop option for mount, if the master is a file
	[ -f "$MASTER" ] && MNT_OPTIONS="-o loop"
	mount $MNT_OPTIONS "$MASTER" "$MASTER_MNT" 2>&1 | indentation

	echo "Removing old bootfs files ..."
	rm -rf "$BOOT_DIR" && mkdir "$BOOT_DIR" 2>&1 | indentation
	
	echo "Copying bootfs files ..."
	local item_dir
	# ignore the big KNOPPIX image
	find "$MASTER_MNT" -type f | sed 's#^$MASTER_MNT/##' | grep -v "^$KNOPPIX_IMAGE_FILE$" | while read item
	    do	item_dir=$(dirname "$item")
	    	[ -e "$BOOT_DIR/$item_dir" ] || mkdir -p "$BOOT_DIR/$item_dir"
		cp -a "$MASTER_MNT/$item" "$BOOT_DIR/$item_dir/"
	  done 2>&1 | indentation
	
	echo "Mounting the compressed image file ..."
	mount "$MASTER_MNT/$KNOPPIX_IMAGE_FILE" "$ROOT_MNT" 2>&1 | intendation
	
	echo "Copying the compressed root system ..."
	cp -a "$ROOT_MNT/." "$ROOT_DIR"

	umount "$ROOT_MNT"
	umount "MASTER_MNT"
}


function master_backup()
{
	umount_procfs
	local root_parent=$(dirname "$ROOT_DIR")
	local root_entry=$(basename "$ROOT_DIR")
	local boot_parent=$(dirname "$BOOT_DIR")
	local boot_entry=$(basename "$BOOT_DIR")
	[ -e "$BACKUP_DIR" ] || mkdir -p "$BACKUP_DIR"
	echo "Creating archive of the boot filesystem ..."
	tar czf "$BACKUP_DIR/bootfs.tar.gz" -C "$boot_parent" "$boot_entry"
	echo "Creating archive of the root filesystem (this may take _very_ long) ..."
	tar czf "$BACKUP_DIR/rootfs.tar.gz" -C "$root_parent" "$root_entry"
}


function master_restore()
{
	umount_procfs
	local boot_backup_file=$BACKUP_DIR/bootfs.tar.gz
	local boot_parent=$(dirname "$BOOT_DIR")
	local root_backup_file=$BACKUP_DIR/rootfs.tar.gz
	local root_parent=$(dirname "$ROOT_DIR")
	# restoring the boot directory (the small one)
	if [ -e "$boot_backup_file" ]
	  then	if [ -e "$BOOT_DIR" ]
		  then	echo "Removing old files from boot filesystem ..."
			rm -rf "$BOOT_DIR"
		 fi
		echo "Unpacking the boot filesystem archive ..."
		tar xzf "$boot_backup_file" -C "$boot_parent"
	  else	echo "Warning: no archive for boot filesystem found ($boot_backup_file) - skipping ..."
	 fi
	# restoring the root directory (the big one)
	if [ -e "$root_backup_file" ]
	  then	if [ -e "$ROOT_DIR" ]
		  then	echo "Removing old files from root filesystem ..."
			rm -rf "$ROOT_DIR"
		 fi
		echo "Unpacking the root filesystem archive ..."
		tar xzf "$root_backup_file" -C "$root_parent"
	  else	echo "Warning: no archive for root filesystem found ($root_backup_file) - skipping ..."
	 fi
}


function remove_packages()
{
	local pkg_list=$(find "$PKG_REMOVE_LIST_DIR" -type f | while read a
	  do	grep -v "^#" "$a" | tr "\n" " "
	 done)
	[ -z "$pkg_list" ] && return
	echo "dpkg --force-all -P $pkg_list 2>&1 | grep -v 'which isn.t installed.'" | chroot_image
}


function umount_procfs()
{
	# check for a mounted procfs
	mount | grep -q " $ROOT_DIR/proc " && umount "$ROOT_DIR/proc"
	true
}


# execute the content of stdin within the chroot environment
# runs an interactive shell, if stdin is empty
function chroot_image()
{
	umount_procfs
	mount -t proc proc "$ROOT_DIR/proc"
	# ignore errors
	set +e
	local action=$@
	[ -z "$action" ] && action=/bin/bash
	LANG=C TERM=linux chroot "$ROOT_DIR" $action
	set -e
	umount "$ROOT_DIR/proc"
	return
}

function create_iso_root()
{
	umount_procfs
	echo "Creating the iso ..."
	mkisofs $MKISOFS_OPTIONS -o "$IMAGE_FILE" "$ROOT_DIR"
	#kisofs -b lib/grub/i386-pc/stage2_eltorito -no-emul-boot -boot-load-size 1 -boot-info-table -R -U -V "silicann_compressed_livecd" --publisher "Silicann GmbH" -hide-rr-moved -cache-inodes -no-bak -pad -o "$IMAGE_FILE" "$ROOT_DIR"
}


function configure()
{
	if [ ! -e "$ROOT_DIR" ]; then 
		echo -e "Directory \"$ROOT_DIR\" not found!"
		echo -e "Did you run \"$0 init\"?" 
		exit 1
	fi

	echo "Copying files ..."
		# svn export --force "$TEMPLATE_DIR/." "$ROOT_DIR"
		# only ignore the owner attribut
		cp -r --preserve=mode,timestamps,links "$TEMPLATE_DIR/." "$ROOT_DIR" 2>&1 | indentation
	
	echo "Configuring ..."
		# filter hidden files - they are usually vi-swap-files :)
		find -L "$SCRIPTS_DIR" -type f | grep -v "/\." | sort | while read scriptname
		    do	echo "Executing $scriptname ..."
		    	cat "$scriptname" | chroot_image
		  done
}


function blanknburn_cdrw()
{
	cdrecord -v dev=$CDWRITER blank=fast
	cdrecord -v dev=$CDWRITER "$IMAGEZ_FILE"
}


function initialization()
{
	# initialize local directories (easier for users)
	for a in "$LOCALCONF_DIR" "$BUILD_DIR" "$SCRIPTS_DIR" "$PKG_INSTALL_LIST_DIR" "$PKG_REMOVE_LIST_DIR" "$TMP_DIR" "$BACKUP_DIR" "$TEMPLATE_DIR" "$IMAGE_DIR"
	    do	if [ ! -e "$a" ]
		   then	echo "Creating directory '$a' ... "
			mkdir -p "$a"
		  fi
	  done
	
}

################ do it! ######################

if [ $# -eq 0 ]
	then	echo "[`basename $0`] - no arguments supplied - maybe you want to use '--help'" >&2
		exit 1
  fi

# check for uid=0 (necessary for all operations except for qemu)
[ "$(id -u)" -ne 0 -a "$*" != "qemuz" ] && echo "this script ($0) has to be called as root" >&2 && exit 1

initialization

while [ $# -gt 0 ]
  do	case "$1" in
	init )
		master_get
		;;
	backup )
		master_backup
		;;
	restore )
		master_restore
		;;
	packages )
		remove_packages
		#install_packages
		;;
	config )
		configure
		;;
	compress )
		[ -e "$IMAGE_FILE" ] || "$0" iso-root
		cat "$IMAGE_FILE" | chroot_image /usr/bin/create_compressed_fs --best - 65536 >"$BOOT_DIR/KNOPPIX/KNOPPIX"
		;;
	iso-boot )
		[ -e "$BOOT_DIR/KNOPPIX/KNOPPIX" ] || "$0" compress
		mkisofs -pad -l -r -J -v -V "silicann_livecd" --publisher "Silicann GmbH" -b boot/isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -c KNOPPIX/boot.cat -hide-rr-moved -o "$IMAGEZ_FILE" "$BOOT_DIR"
		;;
	chroot )
		chroot_image
		;;
	iso-root )
		create_iso_root
		;;
	qemu )
		qemu -cdrom "$IMAGE_FILE" -nics 0
		;;
	qemuz )
		qemu -cdrom "$IMAGEZ_FILE" -nics 0
		;;
	burn )
		blanknburn_cdrw
		;;
	release )
		"$0" init packages config iso-root compress iso-boot
		md5sum "$IMAGEZ_FILE" > "${IMAGE_FILE}.md5sum"
		sha1sum "$IMAGEZ_FILE" > "${IMAGE_FILE}.sha1sum"
		;;
	help|--help )
		echo "Syntax: `basename $0` ( release | init | backup | packages | config | iso-root | iso-boot | compress | burn | help )"
		echo "  (you may specify more than one action)"
		echo
		;;
	* )
		echo -e "unknown action: $1"
		echo
		"$0" help
		exit 1
		;;
	esac
	shift
  done