#!/bin/bash

set -euo pipefail

readonly LIVELABEL=${LIVELABEL:-LiveSystem}
readonly LIVEDIR=${LIVEDIR:-linomad}
readonly HOMELABEL=${HOMELABEL:-LiveData}

readonly INSTALLPATH="/mnt/install.$$"
readonly SQUASHFS_COMPRESSION="gzip lha lzma lzo xz zstd"
readonly IMGSIZE=${IMGSIZE:-7680} #minimal size of the USB device (8 GB > 7.5 GiB)

#exit error codes
readonly CMD_ERROR=1
readonly SIZE_ERROR=2
readonly PART_ERROR=11
readonly FS_ERROR=12
readonly GRUB_ERROR=13
readonly INITRD_ERROR=14

if [ -f $0 ]
then readonly SCRIPTDIR="$(dirname $(dirname $0))/share/live-system"
else readonly SCRIPTDIR="$(dirname $(dirname $(which $0)))/share/live-system"
fi

if [ -d /usr/lib64/grub ]; then #slackware, arch
	readonly GRUBPATH="/usr/lib64/grub"
else #debian
	readonly GRUBPATH="/usr/lib/grub"
fi
readonly CPIO=$(which cpio 2>/dev/null || which bsdcpio)

function get_distro_name() {
	local rootdirectory=$1
	local distro="unsupported"

	if [ -f $rootdirectory/etc/slackware-version ]
	then distro="slackware"
	fi
	if [ -f $rootdirectory/etc/debian_version ]
	then distro="debian"
	fi
	if [ -f $rootdirectory/etc/pacman.conf ];
	then distro="arch"
	fi
	echo $distro
}


function gen_initrd() {
	local rootdirectory=$1
	local livedirectory=$2
	local additionalmodules=${3:-}

	local kv=$(file -L $rootdirectory/boot/$KERNEL | sed 's/^.*, version \([^ ]\+\) .*$/\1/')

	#tree and utilities
	rm -rf $rootdirectory/tmp/initrd-tree/
	mkdir -p $rootdirectory/tmp/initrd-tree/{bin,dev,etc/init.d,lib,mnt,proc,run,sbin,sys,usr/bin,usr/sbin}
	mkdir -p $rootdirectory/tmp/initrd-tree/{home,root}

	local busy_box=$(ls $(pwd)/busybox 2>/dev/null || which busybox 2>/dev/null) #locate or download BusyBox
	if [ -z "$busy_box" ]; then
		if [ "$(uname -m)" == "x86_64" ]; then
			curl -L https://archlinux.org/packages/extra/x86_64/busybox/download/ -o busybox.tar.zst 2>/dev/null
			tar --zstd -xvf busybox.tar.zst usr/bin/busybox
			rm busybox.tar.zst
		else #arm64
			local busy_ver=$(curl -L http://mirror.archlinuxarm.org/aarch64/extra/ 2>/dev/null | grep '"busybox-.*.pkg.tar.xz"' | cut -f2 -d'"')
			curl -L http://mirror.archlinuxarm.org/aarch64/extra/$busy_ver -o busybox.tar.xz 2>/dev/null
			tar -Jxvf busybox.tar.xz usr/bin/busybox
			rm busybox.tar.xz
		fi
		mv usr/bin/busybox .
		rmdir -p usr/bin
		busy_box=$(pwd)/busybox
	fi
	cp $busy_box $rootdirectory/tmp/initrd-tree/usr/bin/

	chroot $rootdirectory/tmp/initrd-tree /usr/bin/busybox --install -s
	$rootdirectory/tmp/initrd-tree/usr/bin/busybox dumpkmap > $rootdirectory/tmp/initrd-tree/etc/keymap
	mknod -m 640 $rootdirectory/tmp/initrd-tree/dev/console c 5 1
	mknod -m 664 $rootdirectory/tmp/initrd-tree/dev/null c 1 3
	echo "root::0:0::/root:/bin/sh" > $rootdirectory/tmp/initrd-tree/etc/passwd
	touch $rootdirectory/tmp/initrd-tree/etc/fstab

	#kernel modules
	local modules="atkbd:i8042" #keyboard
	modules="$modules:ata_piix:sr_mod" #pata / sata, cdrom
	modules="$modules:xhci-pci:ehci-pci:usb-storage" #usb
	modules="$modules:nls_iso8859-1:isofs:nls_cp437:vfat:ext4:loop:overlay:squashfs" #live filesystems
	if [ ! -z "$additionalmodules" ]; then modules="$modules:$additionalmodules"; fi
	mkdir -p $rootdirectory/tmp/initrd-tree/lib/modules/$kv/kernel
	touch $rootdirectory/tmp/initrd-tree/etc/init.d/load_kernel_modules
	local mod; for mod in ${modules//:/ }; do
		local insmodline; chroot $rootdirectory modprobe -S $kv -D $mod | sed '/^builtin /d' | while read insmodline; do
			if ! grep -q "$insmodline" $rootdirectory/tmp/initrd-tree/etc/init.d/load_kernel_modules; then
				echo $insmodline >> $rootdirectory/tmp/initrd-tree/etc/init.d/load_kernel_modules
				mkdir -p $rootdirectory/tmp/initrd-tree/$(dirname ${insmodline#insmod })
				cp $rootdirectory/${insmodline#insmod } $rootdirectory/tmp/initrd-tree/${insmodline#insmod }
			fi
		done
	done
	chmod +x $rootdirectory/tmp/initrd-tree/etc/init.d/load_kernel_modules

	#Arch Linux
	find $rootdirectory/tmp/initrd-tree/lib/modules/$kv/kernel/ -type f -name "*.zst" -exec zstd -qfd {} \;
	find $rootdirectory/tmp/initrd-tree/lib/modules/$kv/kernel/ -type f -name "*.zst" -exec rm -f {} \;
	sed 's/\.zst$//' -i $rootdirectory/tmp/initrd-tree/etc/init.d/load_kernel_modules

	#init scripts
	cat > $rootdirectory/tmp/initrd-tree/init << EOF
#!/bin/sh
/etc/init.d/rcS
if [ "\$mode" == "busybox" ]
then exec /sbin/init
else exec /etc/init.d/rcLive
fi
EOF
	chmod +x $rootdirectory/tmp/initrd-tree/init
	rm -f $rootdirectory/tmp/initrd-tree/linuxrc
	cp $SCRIPTDIR/{rcS,rcLive} $rootdirectory/tmp/initrd-tree/etc/init.d/
	cp $SCRIPTDIR/rcLive-$distro.inc $rootdirectory/tmp/initrd-tree/etc/init.d/rcLive.inc
	chmod +x $rootdirectory/tmp/initrd-tree/etc/init.d/{rcS,rcLive}
	cat > $rootdirectory/tmp/initrd-tree/etc/inittab << EOF
#::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
tty2::askfirst:-/bin/sh
tty3::respawn:/sbin/getty 38400 tty3
tty4::respawn:/sbin/getty 38400 tty4
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
EOF

	cd $rootdirectory/tmp/initrd-tree
	find . | $CPIO -o -H newc | gzip -9c > ../initrd.gz
	cd - >/dev/null
	rm -rf $rootdirectory/tmp/initrd-tree
	mv $rootdirectory/tmp/initrd.gz $livedirectory/$LIVEDIR/initrd.gz
}


function init_live() {
	local rootdirectory=$1
	local livedirectory=$2
	local additionalmodules=${3:-}
	local status=0

	local distro=$(get_distro_name $rootdirectory)
	source $SCRIPTDIR/build-$distro.inc

	mkdir -p $livedirectory/efi/boot
	mkdir -p $livedirectory/$LIVEDIR/modules

	#kernel
	echo "*** Copying kernel $LIVEDIR/vmlinuz ..."
	cp -f $rootdirectory/boot/$KERNEL $livedirectory/$LIVEDIR/vmlinuz

	#initrd
	echo "*** Generating $LIVEDIR/initrd.gz ..."
	gen_initrd $rootdirectory $livedirectory $additionalmodules

	#grub efi image(s)
	echo "*** Generating EFI GRUB efi/boot/bootx64.efi ..."
	grub-mkimage -p /efi/boot -O x86_64-efi -o $livedirectory/efi/boot/bootx64.efi normal search part_gpt ls \
		fat ext2 iso9660 boot configfile linux multiboot || status=$GRUB_ERROR #all_video

	#grub.cfg
	if [ ! -f $livedirectory/efi/boot/grub.cfg ]; then
		echo "*** Generating GRUB configuration efi/boot/grub.cfg ..."
		#echo -e "insmod all_video\nset timeout=5\nset default=4" > $livedirectory/efi/boot/grub.cfg
		echo -e "terminal_output console\nset timeout=5\nset default=4" > $livedirectory/efi/boot/grub.cfg
		declare -A grubentry0=([entry]="Arabic" [locale]="ar_EG.UTF-8" [keymap]="us" [tz]="Asia/Riyadhne ")
		declare -A grubentry1=([entry]="Bengali" [locale]="bn_BD.UTF-8" [keymap]="us" [tz]="Asia/Dacca")
		declare -A grubentry2=([entry]="Chineese" [locale]="zh_CN.UTF-8" [keymap]="us" [tz]="Etc/GMT+8")
		declare -A grubentry3=([entry]="English" [locale]="en_US.UTF-8" [keymap]="us" [tz]="America/New_York")
		declare -A grubentry4=([entry]="French" [locale]="fr_FR.UTF-8" [keymap]="fr" [tz]="Europe/Paris")
		declare -A grubentry5=([entry]="German" [locale]="de_DE.UTF-8" [keymap]="de" [tz]="Europe/Berlin")
		declare -A grubentry6=([entry]="Hindi" [locale]="hi_IN.UTF-8" [keymap]="us" [tz]="Asia/Dili")
		declare -A grubentry7=([entry]="Italian" [locale]="it_IT.UTF-8" [keymap]="it" [tz]="Europe/Rome")
		declare -A grubentry8=([entry]="Japan" [locale]="ja_JP.UTF-8" [keymap]="jp106" [tz]="Asia/Tokyo")
		declare -A grubentry9=([entry]="Portuguese" [locale]="pt_PT.UTF-8" [keymap]="pt" [tz]="Europe/Lisbon")
		declare -A grubentrya=([entry]="Russian" [locale]="ru_RU.UTF-8" [keymap]="ru" [tz]="Europe/Moscow")
		declare -A grubentryb=([entry]="Spanish" [locale]="es_ES.UTF-8" [keymap]="es" [tz]="Europe/Madrid")
		declare -n menuentry
		syshome="sys=$LIVELABEL:$LIVEDIR home=$HOMELABEL"
		for menuentry in ${!grubentry@}; do
			cat >> $livedirectory/efi/boot/grub.cfg << EOF
menuentry "${menuentry[entry]}" {
	linux /$LIVEDIR/vmlinuz $syshome locale=${menuentry[locale]} keymap=${menuentry[keymap]} tz=${menuentry[tz]} hwc=localtime
	initrd /$LIVEDIR/initrd.gz
}
EOF
		done
		cat >> $livedirectory/efi/boot/grub.cfg << EOF
menuentry " " { true }
menuentry "Busybox console" {
	linux /$LIVEDIR/vmlinuz mode=busybox
	initrd /$LIVEDIR/initrd.gz
}
EOF
	fi

	return $status
}


function add_module() {
	local rootdirectory=$1
	local livedirectory=$2
	local modulename=$3
	if [ ! -z "$4" ]; then #gzip lzma lzo lz4 xz zstd
		local compoption="-comp $4"
	else
		local compoption="-comp zstd"
	fi

	mkdir -p $livedirectory/$LIVEDIR/modules
	rm -f $livedirectory/$LIVEDIR/modules/$modulename
	mksquashfs $rootdirectory $livedirectory/$LIVEDIR/modules/$modulename $compoption -e tmp dev proc sys $livedirectory || return $FS_ERROR
}


function create_iso() {
	local livedirectory=$1
	local imagefilename=$2

	echo "*** Generating El Torito EFI boot image ..."
	dd if=/dev/zero of=$livedirectory/efi.img bs=1k count=2048
	mkfs.vfat $livedirectory/efi.img
	mkdir $livedirectory/efi-img
	mount -o loop $livedirectory/efi.img $livedirectory/efi-img
	mkdir -p $livedirectory/efi-img/efi/boot
	cp $livedirectory/efi/boot/bootx64.efi $livedirectory/efi-img/efi/boot/
	echo "search --no-floppy --label $LIVELABEL --set root" > $livedirectory/efi-img/efi/boot/grub.cfg #required for
	cat $livedirectory/efi/boot/grub.cfg >> $livedirectory/efi-img/efi/boot/grub.cfg #USB EFI boot flashed from ISO
	cp $livedirectory/efi/boot/boot*.efi $livedirectory/efi-img/efi/boot/
	umount $livedirectory/efi-img
	rmdir $livedirectory/efi-img

	echo "*** Generating El Torito BIOS (legacy) boot image ..."
	grub-mkimage -p /efi/boot -O i386-pc -o $livedirectory/bios.img.part normal search part_gpt ls \
		fat ext2 iso9660 boot configfile linux multiboot all_video biosdisk || { rm -f $livedirectory/efi.img; return $GRUB_ERROR; }
	cat $GRUBPATH/i386-pc/cdboot.img $livedirectory/bios.img.part > $livedirectory/bios.img || { rm -f $livedirectory/efi.img; return $GRUB_ERROR; }
	rm -f $livedirectory/bios.img.part

	echo "*** Generating EFI bootable ISO $imagefilename ..."
	rm -f $imagefilename
	xorriso -outdev $imagefilename -volid "$LIVELABEL" -map $livedirectory / \
		-boot_image any cat_hidden=on -boot-image any platform_id=0x00 -boot-image grub bin_path=/bios.img \
		-boot_image grub grub2_mbr=$GRUBPATH/i386-pc/boot_hybrid.img -boot-image grub grub2_boot_info=on \
		-boot-image any next \
		-boot-image any appended_part_as=gpt -append_partition 2 0xef $livedirectory/efi.img -boot_image any efi_path=--interval:appended_partition_2:all:: \
		-hide on /bios.img -- -rm_r /efi.img -- || { rm -f $livedirectory/efi.img $livedirectory/bios.img; return $FS_ERROR; }

	rm -f $livedirectory/efi.img $livedirectory/bios.img
}


function create_img() {
	local livedirectory=$1
	local imagefilename=$2
	local systemsize=$3 #in MB

	echo "*** Generating disk image $imagefilename ..."
	dd if=/dev/zero of=$imagefilename bs=1M count=$IMGSIZE

	echo "*** Partitioning $imagefilename ..."
	sgdisk -o -n 1:0:+$systemsize"M" -t 1:ef00 -n 2:0:0 $imagefilename
	
	loop_device=$(losetup -f --show -P $imagefilename)
	install_common_usb_image $livedirectory $loop_device"p1" $loop_device"p2"
	losetup -d $loop_device
	
	echo "*** Compressing $imagefilename ..."
	zstd --rm $imagefilename
}


function install_usb() {
	local livedirectory=$1
	local installdevice=$2
	local systemsize=$3 #in MB

	echo "*** Partitioning $installdevice ..."
	sgdisk -Z $installdevice
	sgdisk -o -n 1:0:+$systemsize"M" -t 1:ef00 -n 2:0:0 $installdevice || return $PART_ERROR
	partprobe $installdevice; sleep 3

	local installpart="/dev/"$(basename /sys/block/${installdevice#/dev}/${installdevice#/dev}*1)
	local datapart="/dev/"$(basename /sys/block/${installdevice#/dev}/${installdevice#/dev}*2)

	return install_common_usb_image $livedirectory $installpart $datapart
}


function install_common_usb_image() {
	local livedirectory=$1
	local installpart=$2
	local datapart=$3

	echo "*** Formating EFI/system partition $installpart ..."
	mkfs.vfat -n "$LIVELABEL" $installpart || return $FS_ERROR
	echo "*** Formating data partition $datapart ..."
	mkfs.ext4 -F -L "$HOMELABEL" -O ^has_journal -O encrypt $datapart || return $FS_ERROR
	sleep 3

	echo "*** Initializing users ..."
	if ! [ -z "$(ls -A $livedirectory/$LIVEDIR/modules/)" ]; then
		mkdir -p $INSTALLPATH/{system,modules,home}
		mount $datapart $INSTALLPATH/home
		local modulesbranches=""
		local module; for module in $livedirectory/$LIVEDIR/modules/*; do
			local modulename=$(basename $module)
			modulesbranches="$modulesbranches:$INSTALLPATH/modules/$modulename"
			mkdir $INSTALLPATH/modules/$modulename
			mount -o loop -t squashfs $module $INSTALLPATH/modules/$modulename 2>/dev/null
		done
		modulesbranches=${modulesbranches#:}
		if (( $(ls $INSTALLPATH/modules/ | wc -w) == 1 ))
		then mount --bind $modulesbranches $INSTALLPATH/system
		else mount -t overlay -o ro,lowerdir=$modulesbranches overlay $INSTALLPATH/system
		fi
		local entry; sed -n '\@:/home/@p' $INSTALLPATH/system/etc/passwd | sed -e /^ftp:/d -e /nologin$/d | while read entry; do
			local uid=$(echo $entry | cut -f3 -d:)
			local gid=$(echo $entry | cut -f4 -d:)
			local homedir=$(echo $entry | cut -f6 -d:)
			cp -dpR $INSTALLPATH/system/etc/skel $INSTALLPATH/$homedir
			chown -R $uid:$gid $INSTALLPATH/$homedir
		done
		umount $INSTALLPATH/system
		umount $INSTALLPATH/home
		local mb; for mb in ${modulesbranches//:/ }; do umount $mb; done
		rm -rf $INSTALLPATH
	fi

	update_usb $livedirectory $installpart
}

function update_usb() {
	local livedirectory=$1
	local installpart=$2

	echo "*** Copying live system files ..."
	mkdir -p $INSTALLPATH; mount $installpart $INSTALLPATH
	rm -rf $INSTALLPATH/{efi,$LIVEDIR}
	cp -r $livedirectory/* $INSTALLPATH/
	umount $INSTALLPATH; rmdir $INSTALLPATH
}

function enable_bios() {
	local installdevice=$1

	echo "*** Enabling MBR (legacy partition table) ..."
	if [ -b $installdevice\128 ]; then
		sgdisk -t 128:ef02 -h 2 $installdevice
	else
		sgdisk -n 128:34:2047 -t 128:ef02 -h 2 $installdevice || return $PART_ERROR
	fi
	partprobe $installdevice; sleep 3

	echo "*** Enabling BIOS (legacy) boot ..."
	local installpart=$(lsblk -r -o PATH,LABEL $installdevice | grep "$LIVELABEL" | cut -f1 -d' ')
	mkdir -p $INSTALLPATH; mount $installpart $INSTALLPATH
	rm -rf $INSTALLPATH/bios
	grub-install --target i386-pc --boot-directory $INSTALLPATH/bios $installdevice || { umount $INSTALLPATH; rmdir $INSTALLPATH; return $GRUB_ERROR; }
	echo "configfile /efi/boot/grub.cfg" > $INSTALLPATH/bios/grub/grub.cfg
	umount $INSTALLPATH; rmdir $INSTALLPATH
}


function install_system() {
	local rootdirectory=$1
	local systempart=$2

	local distro=$(get_distro_name $rootdirectory)
	source $SCRIPTDIR/build-$distro.inc

	echo "*** Formating system partition $systempart ..."
	mkfs.ext4 -F $systempart || return $FS_ERROR
	mkdir -p $INSTALLPATH; mount $systempart $INSTALLPATH

	#copy
	echo "*** Copying system files ..."
	local directory; for directory in $rootdirectory/*; do
		cp -a $directory $INSTALLPATH/
	done
	mkdir -p $INSTALLPATH/{dev,proc,sys,tmp}

	#initrd
	echo "*** Generating initial RAM disk ..."
	setup_initrd $systempart || { umount $INSTALLPATH; rmdir $INSTALLPATH; return $INITRD_ERROR; }

	#fstab
	local uuid=$(lsblk -o UUID $systempart | sed 1d)
	echo "*** Adding fstab entry 'UUID=$uuid ($systempart) / ext4' ..."
	cat > $INSTALLPATH/etc/fstab << EOF
#$systempart = $uuid
UUID=$uuid / ext4 defaults 1 1
tmpfs /dev/shm tmpfs defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
EOF

	#setup
	echo "*** Configuring language, time and keymap ..."
	setup_locale
	setup_time
	setup_console_keymap
	if [ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]; then #Xorg
		cp -f /etc/X11/xorg.conf.d/00-keyboard.conf $INSTALLPATH/etc/X11/xorg.conf.d/
	fi
	if [ -f /etc/dconf/db/local.d/00-keyboard ]; then #GNOME
		cp /etc/dconf/db/local.d/00-keyboard $INSTALLPATH/etc/dconf/db/local.d/
		chroot $INSTALLPATH dconf update
	fi
	if [ -f /etc/hypr/keymap.conf ]; then #Hyprland (Wayland)
		cp -f /etc/hypr/keymap.conf $INSTALLPATH/etc/hypr
	fi

	if [ -d  /etc/NetworkManager/system-connections ]; then
		echo "*** Copying WiFi configuration ..."
		mkdir -p $INSTALLPATH/etc/NetworkManager/system-connections
		cp -f /etc/NetworkManager/system-connections/* $INSTALLPATH/etc/NetworkManager/system-connections/
		chmod -R go-rwx $INSTALLPATH/etc/NetworkManager/system-connections
	fi

	umount $INSTALLPATH; rmdir $INSTALLPATH
}


function setup_grub() {
	local systempart=$1
	local status=0

	mkdir -p $INSTALLPATH; mount $systempart $INSTALLPATH
	mkdir -p $INSTALLPATH/boot/grub
	local installdevice="/dev/"$(ls -d /sys/block/*/$(basename $systempart) | cut -f4 -d'/')
	local efipartition=$(lsblk -o PATH,PARTTYPE -rn $installdevice | sed -n '/ c12a7328-f81f-11d2-ba4b-00a0c93ec93b$/p' | cut -f1 -d' ')

	if [ ! -z "$efipartition" ]; then #GPT -> EFI boot
		local efibind="sys/firmware/efi/efivars"
		mkdir -p $INSTALLPATH/boot/efi
		mount $efipartition $INSTALLPATH/boot/efi
		for d in sys proc dev $efibind; do mount --bind /$d $INSTALLPATH/$d; done
		echo "*** Installing EFI GRUB on $efipartition ..."
		chroot $INSTALLPATH grub-install --target=$(uname -m)-efi || status=$GRUB_ERROR
		for d in $efibind dev proc sys; do umount $INSTALLPATH/$d; done
		umount $INSTALLPATH/boot/efi
		local uuid=$(lsblk -o UUID $efipartition | sed 1d)
		echo "*** Adding fstab entry 'UUID=$uuid ($efipartition) /boot/efi vfat' ..."
		sed -e /\\/boot\\/efi/d -e/$uuid/d -i $INSTALLPATH/etc/fstab
		echo -e "#$efipartition = $uuid\nUUID=$uuid /boot/efi vfat defaults 1 1" >> $INSTALLPATH/etc/fstab
	else
		local efibind=""
		if lsblk -o PTTYPE,PARTTYPE -rn $installdevice | grep -q "^dos "; then #MBR -> BIOS (legacy) boot
			for d in sys proc dev; do mount --bind /$d $INSTALLPATH/$d; done
			echo "*** Installing BIOS (legacy) GRUB on $installdevice ..."
			chroot $INSTALLPATH grub-install --force --target i386-pc $installdevice || status=$GRUB_ERROR
			for d in dev proc sys; do umount $INSTALLPATH/$d; done
		fi
	fi

	echo "*** Configuring GRUB menu ..."
	sed -e /GRUB_SAVEDEFAULT/d -e /GRUB_DEFAULT/d -i $INSTALLPATH/etc/default/grub
	echo -e "GRUB_SAVEDEFAULT=true\nGRUB_DEFAULT=saved" >> $INSTALLPATH/etc/default/grub
	for d in sys proc dev $efibind; do mount --bind /$d $INSTALLPATH/$d; done
	chroot $INSTALLPATH grub-mkconfig -o /boot/grub/grub.cfg || status=$GRUB_ERROR
	for d in $efibind dev proc sys; do umount $INSTALLPATH/$d; done

	umount $INSTALLPATH; rmdir $INSTALLPATH
	return $status
}


function setup_home() {
	local systempart=$1
	local homepart=$2
	local login=$3
	local password=$4

	mkdir -p $INSTALLPATH; mount $systempart $INSTALLPATH
	mount $homepart $INSTALLPATH/home

	local distro=$(get_distro_name $INSTALLPATH)
	source $SCRIPTDIR/build-$distro.inc

	local fs=$(mount | grep "$homepart" | cut -f5 -d' ')
	local uuid=$(lsblk -o UUID $homepart | sed 1d)
	echo "*** Adding fstab entry 'UUID=$uuid ($homepart) /home $fs' ..."
	sed -e /\\/home/d -e/$uuid/d -i $INSTALLPATH/etc/fstab
	echo -e "#$homepart = $uuid\nUUID=$uuid /home $fs defaults 1 1" >> $INSTALLPATH/etc/fstab

	local user; for user in $(sed -n '\@:/home/@p' $INSTALLPATH/etc/passwd | sed -e /false$/d -e /nologin$/d | cut -f1 -d:); do #for all users
		if [ ! -d $INSTALLPATH/home/$user ]; then
			echo "*** Disabling user $user ..."
			chroot $INSTALLPATH userdel $user
		fi
	done

	if [ -d $INSTALLPATH/home/$login ]; then #home exists
		local homeuid=$(ls -nd $INSTALLPATH/home/$login | cut -f3 -d' ')
		local homegid=$(ls -nd $INSTALLPATH/home/$login | cut -f4 -d' ')
		if ! grep -q "^$login:" $INSTALLPATH/etc/passwd; then #login doesn't exists
			echo "*** Enabling user $login (home directory exists) ..."
			chroot $INSTALLPATH useradd -g $homegid -u $homeuid -G $RIGHT_GROUPS $login
		fi
	else
		echo "*** Creating user ..."
		chroot $INSTALLPATH useradd -m -g users -G $RIGHT_GROUPS $login
	fi
	echo "*** Setting password ..."
	echo -e "root:$password\n$login:$password" | chroot $INSTALLPATH chpasswd

	umount $INSTALLPATH/home
	umount $INSTALLPATH; rmdir $INSTALLPATH
}


function repair_system() {
	local systempart=$1

	echo "*** Checking '$systempart' (root partition) ..."
	fsck -f $systempart || return $FS_ERROR

	#EFI partition filesystem check
	local installdevice="/dev/"$(ls -d /sys/block/*/$(basename $systempart) | cut -f4 -d'/')
	local efipartition=$(lsblk -o PATH,PARTTYPE -rn $installdevice | sed -n '/ c12a7328-f81f-11d2-ba4b-00a0c93ec93b$/p' | cut -f1 -d' ')
	if [ ! -z "$efipartition" ]; then
		echo "*** Checking '$efipartition' (efi partition) ..."
		fsck.vfat -a $efipartition || return $FS_ERROR
	else
		if lsblk -o PTTYPE,PARTTYPE -rn $installdevice | grep -q "^gpt "; then
			echo "*** Error: '$installdevice' is partitionned using GPT without EFI partition!"
		else
			echo "*** Notice: '$installdevice' is partitionned using MBR (BIOS/legacy boot)."
		fi
	fi

	mkdir -p $INSTALLPATH; mount $systempart $INSTALLPATH
	#fstab
	local uuid=$(lsblk -o UUID $systempart | sed 1d)
	local fs=$(mount | grep "$systempart" | cut -f5 -d' ')
	local tabfs=$(cat $INSTALLPATH/etc/fstab | sed -n "\@^UUID=$uuid\|^$systempart@p" | sed 's/^\([0-9]\+\).*$/\1/')
	if [ "$fs" != "$tabfs" ]; then
		echo "*** Fixing fstab entry for root partition ('$systempart') ..."
		sed -e '/ \/ /d' -i $INSTALLPATH/etc/fstab
		echo -e "#$systempart = $uuid\nUUID=$uuid / $fs defaults 1 1" >> $INSTALLPATH/etc/fstab
	fi

	#home partition filesystem check
	local homepart=$(cat $INSTALLPATH/etc/fstab | sed -n "\@/home[^/]@p" | sed 's/^\([0-9]\+\).*$/\1/')
	if [ ! -z "$homepart" ]; then
		echo fsck -f $homepart || { umount $INSTALLPATH; rmdir $INSTALLPATH; return $FS_ERROR; }
	else
		echo "*** Notice: No '/home' partition configured in '/etc/fstab'"
	fi

	#initrd
	local distro=$(get_distro_name $INSTALLPATH)
	source $SCRIPTDIR/build-$distro.inc
	echo "*** Generating initial RAM disk ..."
	setup_initrd $systempart || { umount $INSTALLPATH; rmdir $INSTALLPATH; return $INITRD_ERROR; }

	umount $INSTALLPATH; rmdir $INSTALLPATH
}


action=${1:-}
case $action in
"--init")
	rootdirectory=${2:-}
	livedirectory=${3:-}
	additionalmodules=${4:-}
	if [ -d "$rootdirectory" ] && [ ! -z "$livedirectory" ]; then
		init_live $rootdirectory $livedirectory $additionalmodules
		exit $?
	else
		echo -e "Setups kernel, initRamFs, GRUB EFI image and configuration file.\n"
		echo "usage: $(basename $0) --init root_dir live_dir [additional_modules]"
		echo "example: $(basename $0) --init system /live"
		exit $CMD_ERROR
	fi
	;;

"--module")
	rootdirectory=${2:-}
	livedirectory=${3:-}
	modulename=${4:-}
	compression=${5:--zstd}
	compression=${compression#-}
	if ! echo $SQUASHFS_COMPRESSION | grep -q "$compression"; then
		echo "error: invalid compression option '$compression'"
		exit $CMD_ERROR
	fi
	 #removes leading dash
	if [ -d "$rootdirectory" ] && [ -d "$livedirectory" ] && [ ! -z "$modulename" ]; then
		add_module $rootdirectory $livedirectory $modulename $compression
		exit $?
	else
		echo -e "Creates a SquashFS module of the system.\n"
		echo "usage: $(basename $0) --module root_dir live_dir module_name [-gzip|...]"
		echo -e "\tavailable compressions: $SQUASHFS_COMPRESSION (default)"
		echo "example: $(basename $0) --module system live 0-main"
		exit $CMD_ERROR
	fi
	;;

"--iso")
	livedirectory=${2:-}
	imagefilename=${3:-}
	if [ -d "$livedirectory" ] && [ -d "$(dirname $imagefilename)" ] && [ ! -d "$imagefilename" ]; then
		create_iso $livedirectory $imagefilename
		exit $?
	else
		echo -e "Creates a live CD/DVD ISO from live system.\n"
		echo "usage: $(basename $0) --iso live_dir iso_file"
		echo "example: $(basename $0) --iso live live.iso"
		exit $CMD_ERROR
	fi
	;;

"--img")
	livedirectory=${2:-}
	imagefilename=${3:-}
	systemsize=${4:-}
	if [ -d "$livedirectory" ] && [ -d "$(dirname $imagefilename)" ] && [ ! -d "$imagefilename" ]; then
		if [ -z "$systemsize" ]; then
			livesize=$(du -sm "$livedirectory" | awk '{ print $1 }')
			let reservedsize=livesize*2
			for systemsize in 2048 4096 8192 16384 32768; do if (( $systemsize > $reservedsize )); then break; fi; done
		fi
		create_img $livedirectory $imagefilename $systemsize
	else
		echo -e "Creates a live disk image from live system.\n"
		echo "usage: $(basename $0) --img live_dir img_file [systemsizeMB]"
		echo "example: $(basename $0) --img live live.img"
		exit $CMD_ERROR
	fi
	;;

"--usb")
	livedirectory=${2:-}
	installdevice=${3:-}
	systemsize=${4:-}
	if [ -d "$livedirectory" ] && [ -b "$installdevice" ] && [ -d /sys/block/$(basename $installdevice) ]; then
		livesize=$(du -sm "$livedirectory" | awk '{ print $1 }')
		installpart=$(lsblk -r -o PATH,LABEL $installdevice | sed -n "/$LIVELABEL/p" | cut -f1 -d' ') || installpart=""
		if [ -z "$systemsize" ] && echo $installpart | grep -q "[0-9]$"; then #update
			echo "*** Checking '$installpart' (live system) ..."
			fsck.vfat -a $installpart || exit $FS_ERROR
			datapart=$(lsblk -r -o PATH,LABEL $installdevice | sed -n "/$HOMELABEL/p" | cut -f1 -d' ') || datapart=""
			if [ ! -z "$datapart" ]; then
				echo "*** Checking '$datapart' (persistent storage) ..."
				fsck -f $datapart || exit $FS_ERROR
			fi
			update_usb $livedirectory $installpart
			exit $?
		else #partition, format & install
			mediasize=$(cat /sys/block/$(basename $installdevice)/size)
			if [ -z "$systemsize" ]; then
				let reservedsize=livesize*2
				for systemsize in 2048 4096 8192 16384 32768; do if (( $systemsize > $reservedsize )); then break; fi; done
			fi
			if (( $systemsize < $mediasize )); then
				echo -n "Live-USB install will wipe everything on '$installdevice'; continue [y,N]? "; read confirm
				if [ "$confirm" == "y" ]; then
					echo "install_usb $livedirectory $installdevice $systemsize"
					install_usb $livedirectory $installdevice $systemsize
				fi
			else
				echo "error: insufficient space: $livesize MB needed / $systemsize MB provided ($mediasize MB available)"
				exit $SIZE_ERROR
			fi
		fi
	else
		echo -e "Copies live system on USB device.\n"
		echo "usage: $(basename $0) --usb live_dir device [systemsizeMB]"
		echo "example: $(basename $0) --usb live /dev/sdx 8192"
		exit $CMD_ERROR
	fi
	;;

"--bios")
	installdevice=${2:-}
	if [ -b "$installdevice" ] && [ -d /sys/block/$(basename $installdevice) ]; then
		installpart=$(lsblk -r -o PATH,LABEL $installdevice | sed -n "/$LIVELABEL/p" | cut -f1 -d' ')
		if echo $installpart | grep -q "[0-9]$"; then
			echo "*** Checking '$installpart' (live system) ..."
			fsck.vfat -a $installpart || exit $FS_ERROR
			enable_bios $installdevice
			exit $?
		else
			echo "error: no '$LIVELABEL' partition found on '$installdevice'"
			exit $PART_ERROR
		fi
	else
		echo -e "Enables BIOS boot on USB device.\n"
		echo "usage: $(basename $0) --bios device"
		echo "example: $(basename $0) --bios /dev/sdx"
		exit $CMD_ERROR
	fi
	;;

"--install")
	rootdirectory=${2:-}
	systempart=${3:-}
	installdevice="/dev/"$(ls -d /sys/block/*/$(basename $systempart) | cut -f4 -d'/')
	if [ -d "$rootdirectory" ] && [ -b "$systempart" ]; then
		echo -n "System install will wipe everything on '$systempart'; continue [y,N]? "; read confirm
		if [ "$confirm" == "y" ]; then
			efipartition=$(lsblk -o PATH,PARTTYPE -rn $installdevice | sed -n "/ c12a7328-f81f-11d2-ba4b-00a0c93ec93b$/p" | cut -f1 -d' ')
			if [ ! -z "$efipartition" ]; then
				echo "*** Checking '$efipartition' (efi partition) ..."
				fsck.vfat -a $efipartition || exit $FS_ERROR
			fi
			install_system $rootdirectory $systempart && setup_grub $systempart
			exit $?
		fi
	else
		echo -e "Install system and GRUB boot loader.\n"
		echo "usage: $(basename $0) --install root_dir partition"
		echo "example: $(basename $0) --install /live/system /dev/sdx2"
		exit $CMD_ERROR
	fi
	;;

"--repair")
	systempart=${2:-}
	if [ -b "$systempart" ]; then
		repair_system $systempart && setup_grub $systempart
		exit $?
	else
		echo -e "Repair installed system (filesystem, initramfs, grub).\n"
		echo "usage: $(basename $0) --repair partition"
		echo "example: $(basename $0) --repair /dev/sdx2"
		exit $CMD_ERROR
	fi
	;;

"--home")
	systempart=${2:-}
	homepart=${3:-}
	login=${4:-}
	password=${5:-}
	if [ -b "$systempart" ] && [ -b "$homepart" ] && [ "$homepart" != "$systempart" ] && [ ! -z "$login" ] && [ ! -z "$password" ] ; then
		echo "*** Checking '$systempart' (root partition) ..."
		fsck -f $systempart || exit $FS_ERROR
		echo "*** Checking '$homepart' (home partition) ..."
		fsck -f $homepart || exit $FS_ERROR
		setup_home $systempart $homepart $login "$password"
		#exit $? (no 'return $ERROR_*')
	else
		echo -e "Setups /home, user login and password.\n"
		echo "usage: $(basename $0) --home install_part home_part login password"
		echo "example: $(basename $0) --home /dev/sdx2 /dev/sdx4 wolf b0seMolv"
		exit $CMD_ERROR
	fi
	;;

*)  echo "$(basename $0) --init root_dir live_dir [additional_modules]"
	echo "$(basename $0) --module root_dir live_dir module_name [-gzip|-xz|...]"
	echo "$(basename $0) --iso live_dir iso_file"
	echo "$(basename $0) --img live_dir img_file"
	echo "$(basename $0) --usb live_dir device [systemsizeMB]"
	echo "$(basename $0) --bios device"
	echo "$(basename $0) --install root_dir partition"
	echo "$(basename $0) --repair partition"
	echo "$(basename $0) --home install_part home_part login password"

	echo ""
	echo "variables influencing the script behavior:"
	echo "- LIVELABEL ($LIVELABEL): live-system filesystem label"
	echo "- LIVEDIR ($LIVEDIR): live-system directory name"
	echo "- HOMELABEL ($HOMELABEL): persistent data filesystem label"
	exit $CMD_ERROR
	;;
esac
