#!/bin/ash
# Fatdog64 ISO build script
# (C) James Budiono 2012, 2014, 2016
# License: GPL Version 3 or later.
#
# $1 - building sfs: base/devx/nls/single/multi
# $1 - building iso: iso [std/mini/small/micro] [uefi/xorriso-uefi/grldr/isolinux]
#      (small is the same as mini)
# $1 - building initrd: initrd [std/mini/small/micro]
#

### configuration
LANG=C
FATDOG_VERSION=${FATDOG_VERSION:-702} # update for new release
KERNEL_VERSION=${KERNEL_VERSION-3.18.7} # update for new release
SQUASHFS_PARAM=${SQUASHFS_PARAM--comp xz -Xbcj x86} # inherit 

CHECKSUM=${CHECKSUM:-compute}  # compute, download-once, download-always
PKG_CACHE=${PKG_CACHE:-output}
PKG_URL=${PKG_URL:-http://distro.ibiblio.org/fatdog/packages/700}
OUTPUT_DIR=${OUTPUT_DIR:-iso}
BASE_SFS=${BASE_SFS:-fd64.sfs} # must match BASE_SFS_DEFAULT_PATH in initrd
TARGET_ISO=${TARGET_ISO:-fatdog.iso}
#USE_EXISTING=1 # don't build sfs-es, use existing

# modules required for boot, separated by spaces
BASE_MODULES='cryptoloop|nbd|cifs|aufs|usb-storage|ohci|uhci|ehci|aes|xts|dm-crypt|dm-snapshot|sg|hid-generic' 
BUILD_CONFIG=${BUILD_CONF:-./build-iso.conf}

# working dirs
WORKDIR=${WORKDIR:-/tmp/build-iso.$$}

### standard configuration items - dont change unless you know what you're doing
[ -e "$BUILD_CONFIG" ] && . "$BUILD_CONFIG"
unset BUILD_CONFIG # don't pass through to slave scripts

# pass through all other build-multi-sfs.sh options (except those overriden above)
for p in PKG_EXT PKG_URL PKG_CACHE OUTPUT_DIR INSTALLER_PKG FATDOG_VERSION \
         BASE_PKG_LIST DEVX_PKG_LIST BASE_SFS DEVX_SFS NLS_SFS \
         BASE_ROOTFS_DIR DEVX_ROOTFS_DIR NLS_ROOTFS_DIR KEEP_ROOTFS \
         SQUASHFS_PARAM GEN_EXCLUSION_LIST; do
	eval test \"\$$p\" && eval export $p
done
### end of configuration



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

CLEANUP_DONE=
trap 'cleanup; exit' INT TERM HUP 0
cleanup() {
	if [ -z $CLEANUP_DONE ]; then
		rm -rf "$WORKDIR"
	fi
	CLEANUP_DONE=1
}

run_in_my_dir() {
        mydir=$(readlink -f "$0")
        mydir=${mydir%/*}
        cd $mydir
}

update_checksum() {
	case $CHECKSUM in
		compute)
			(
			echo updating md5sum
			cd $PKG_CACHE
			pkg2repo MD5
			) 
			;;
		download-once)
			! [ -f "${PKG_URL}/CHECKSUMS.md5" ] &&
			wget -N -P $PKG_CACHE "${PKG_URL}/CHECKSUMS.md5"
			;;
		download-always)
			wget -N -P $PKG_CACHE "${PKG_URL}/CHECKSUMS.md5"
			;;
	esac
}

# $* combine all sfses, the last one is target
combine_sfs() {
	echo combining sfs ...
	local target TMPDIR=$WORKDIR/squashfs-root

	for target; do :; done;	rm -f $target
	for p; do
		[ -r "$p" ] && unsquashfs -f -d $TMPDIR "$p" >/dev/null
	done
	mksquashfs $TMPDIR $target -noappend $SQUASHFS_PARAM >/dev/null
	rm -rf $TMPDIR
}

# $1-source, $2-target $3-module list
extract_base_modules() {
	echo extracting kernel modules ...
	local TMPDIR=$WORKDIR/squashfs-root OLD_DEP="$BASE_MODULES" DEP="" KVER
	
	# extract, get kernel ver, update base modules list
	unsquashfs -f -d $TMPDIR "$1" > /dev/null
	KVER=$(ls $TMPDIR/lib/modules | head -n1)
	update_base_module_list $TMPDIR/lib/modules/$KVER/modules.dep

	# delete unnecessary files
	#echo $(echo /$BASE_MODULES | sed 's%|%.ko|/%g')
	find $TMPDIR/lib/modules/$KVER -type f | grep -vE $(echo /$BASE_MODULES | sed 's%|%.ko|/%g; s%$%.ko%') | xargs rm 
	find $TMPDIR/lib/modules/$KVER -type d | sort -r | xargs rmdir 2>/dev/null # remove the empty dirs
	rm -r $TMPDIR/lib/firmware # hopefully no firmware is needed
	
	# re-pack
	depmod -b $TMPDIR $KVER
	mksquashfs $TMPDIR "$2" -noappend $SQUASHFS_PARAM >/dev/null
	rm -rf $TMPDIR
}
# input: $BASE_MODULES, $1-path to modules.dep, returns: BASE_MODULES
update_base_module_list() {
	local DEP OLD_DEP
	DEP="$BASE_MODULES"; OLD_DEP=""
	[ -z "$DEP" ] && return
	while [ "$OLD_DEP" != "$DEP" ]; do	
		OLD_DEP="$DEP"
		for p in $(grep -E $(echo "/$DEP" | sed 's%|%.ko:|/%g; s%$%.ko:%') "$1" | sed 's/.*://'); do
			p=${p##*/}; p=${p%.ko}
			case $DEP in 
				*\|$p*) ;;
				*) DEP="$DEP|$p" ;;
			esac
		done
	done
	BASE_MODULES="$DEP"
}



############ sfs builders #################

# $1-all,base,devx,nls (default all)
build_multi_sfs() {
	update_checksum
	case $1 in
		base) export NODEVX=1 NONLS=1  ;;
		devx) export NOBASE=1 NONLS=1  ;;
		nls)  export NOBASE=1 NODEVX=1 ;;
	esac
	./build-multi-sfs.sh
}

build_single_sfs() {
	update_checksum
	SFS_OUTPUT=$BASE_SFS ./build-sfs.sh
}




############# initrd builders ###############

# $1-std,mini/small,micro $2-basesfs $3-output dir
build_initrd() {
	INITRD_DIR="$WORKDIR/initrd.$$"
	cp -a fatdog-initrd $INITRD_DIR
	
	# kernel modules
	cp $INITRD_DIR/usr/share/blank.sfs $INITRD_DIR/kernel-modules.sfs
	[ -e kernel/kernel-modules.sfs ] && cp kernel/kernel-modules.sfs $INITRD_DIR
	
	# basesfs
	echo building initrd $1
	case $1 in
		std|"") # std is default
			# basesfs inside  initrd
			cp "$2" "$INITRD_DIR"/$BASE_SFS  
			;; 
			
		mini|small) 
			# basesfs outside initrd
			cp "$2" "$3"
			set_external_initrd /$BASE_SFS
			;;
			
		micro)
			# combine base + kernel modules, keep only important modules in initrd
			combine_sfs "$2" kernel/kernel-modules.sfs "$3"/$BASE_SFS
			extract_base_modules kernel/kernel-modules.sfs $INITRD_DIR/kernel-modules.sfs "$BASE_MODULES"
			set_external_initrd
			;;
	esac
	
	# cleanup
	find $INITRD_DIR -type f -name 'placeholder_for_fossil' -delete
	
	# build initrd
	INITRD=$(readlink -f "$3")/initrd
	( cd $INITRD_DIR; find . | cpio -o -H newc > $INITRD )
}
# change default base_sfs from "initrd" to "local", increase waitdev delay to 3 seconds
# $1-basesfs
set_external_initrd() {
	sed -i -e "
	s|^BASE_SFS_DEFAULT_PATH=.*|BASE_SFS_DEFAULT_PATH=$1|
	s|^BASE_SFS_DEFAULT=.*|BASE_SFS_DEFAULT=local|
	s|^DEFAULT_DEVICE_DELAY=.*|DEFAULT_DEVICE_DELAY=3|
	" "$INITRD_DIR"/sbin/system-init 
}



########### iso builder #################

# $1-uefi,xorriso-uefi,grldr,isolinux $2-iso-root $3-output dir
make_iso() {
	local mode=$1
	
	# if mode is uefi, check if efiboot.img exist
	# otherwise build standard iso
	case $mode in
		*uefi*|"")
			! [ -f "$2"/efiboot.img ] && 
			echo "efiboot.img missing, building standard isohybrid." &&
			mode=isolinux ;;
	esac
	
	case $mode in
		uefi|"") # uefi is default
			# uefi build using cdrtools' mkisofs
			rm -f "$2"/grldr "$3/$TARGET_ISO"
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R \
			-b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table \
			-eltorito-alt-boot -eltorito-platform efi -b efiboot.img -no-emul-boot \
			"$2"/
			isohybrid -u "$3/$TARGET_ISO"
			echo UEFI ISO created.
			;;

		# isohybrid uefi builds - still not working correctly with iso 1999 in Windows
		xorriso-uefi) # for recent xorriso (tested with 1.4.0)
			rm -f "$2"/grldr "$3/$TARGET_ISO"
			xorriso -dev "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-compliance "iso_9660_level=3:iso_9660_1999" \
			-map "$2" / \
			-boot_image isolinux dir=/	 \
			-boot_image isolinux system_area=/usr/share/syslinux/isohdpfx.bin \
			-boot_image isolinux partition_table=on \
			-boot_image isolinux next \
			-boot_image any efi_path=efiboot.img \
			-boot_image isolinux partition_entry=gpt_basdat
			echo UEFI ISO created.
			;;
		
		xorriso-uefi-old) # for older xorriso (requires post-processing with isohybrid)
			rm -f "$2"/grldr "$3/$TARGET_ISO"
			xorriso -dev "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-compliance "iso_9660_level=3:iso_9660_1999" \
			-map "$2" / \
			-boot_image isolinux dir=/	 \
			-boot_image isolinux next \
			-boot_image any efi_path=efiboot.img	 
			isohybrid -u "$3/$TARGET_ISO"
			echo UEFI ISO created.
			;;

		grldr)
			# grub build - standard ISO (not isohybrid)
			rm -f "$3/$TARGET_ISO"
			( cd "$2"; rm -rf efiboot.img grub.cfg isolinux.* vesamenu.c32 help )
			cat > "$2"/menu.lst << "EOF"
timeout 10
title Fatdog64 Live CD
kernel /vmlinuz
initrd /initrd
EOF
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R  \
			-b grldr -no-emul-boot -boot-load-size 4 -boot-info-table \
			"$2"/
			echo Grub4dos ISO created.
			;;

		isolinux)
			# standard build with isohybrid
			rm -f "$2"/efiboot.img "$2"/grub.cfg "$2"/grldr "$3/$TARGET_ISO"
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R  \
			-b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table \
			"$2"/
			isohybrid -o 64 "$3/$TARGET_ISO"
			echo Standard ISO created.	
			;;
	esac
}

# $1-std,mini/small,micro (default std) $2-uefi,xorriso-uefi,grldr,isolinux (default uefi), or initrd
build_iso() {
	ISO_ROOT="$WORKDIR/iso-root.$$"
	cp -a fatdog-iso-root $ISO_ROOT
	
	# kernel
	KERNEL_VERSION=$KERNEL_VERSION ./get-kernel.sh 
	! [ -e kernel/vmlinuz ]  && echo "Can't download kernel, exiting..." && exit
	cp kernel/vmlinuz $ISO_ROOT
	
	# sfs, initrd, and iso
	[ -z "$USE_EXISTING" ] && build_multi_sfs base
	build_initrd "$1" "$OUTPUT_DIR/$BASE_SFS" "$ISO_ROOT"
	if [ "$2" = "initrd" ]; then
		mv "$ISO_ROOT"/initrd "$OUTPUT_DIR"
	else
		make_iso "$2" "$ISO_ROOT" "$OUTPUT_DIR"	
	fi
}



#### main ####
run_in_my_dir
mkdir -p "$WORKDIR" "$OUTPUT_DIR"
case $1 in
	multi)   build_multi_sfs      ;;
	single)	 build_single_sfs     ;;
	base)    build_multi_sfs base ;;
	devx)    build_multi_sfs devx ;;
	nls)     build_multi_sfs nls  ;;
	iso)     build_iso "$2" "$3"  ;; # $2-initrd-type $3-iso-type
	initrd)  build_iso "$2" initrd "$WORKDIR" ;; # $2-initrd-type
	*) echo "Usage: ${0##*/} [iso|multi|single|base|devx|nls|initrd]"
	   echo "  iso    options: [std|small/mini|micro] [uefi|xorriso-uefi|grldr|isolinux]"
	   echo "  initrd options: [std|small/mini|micro] "
	   ;;
esac
