#!/bin/bash

set -euo pipefail

readonly LIVELABEL=${LIVELABEL:-LiveSystem}
readonly LINOMAD=${LINOMAD:-linomad}
readonly HOMELABEL=${HOMELABEL:-}

readonly INSTALL_PATH="/mnt/install.$$"
readonly SQUASHFS_COMPRESSION="gzip lz4 lzma lzo xz zstd"
readonly EFI_SIZE="100M"

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

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

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

if [ "$(uname -m)" == "aarch64" ]; then
	readonly GRUB_EFI_TARGET="arm64-efi"
	readonly EFI_BOOT_IMAGE="bootaa64.efi"
else
	readonly GRUB_EFI_TARGET="x86_64-efi"
	readonly EFI_BOOT_IMAGE="bootx64.efi"
fi

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

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


function gen_initrd() {
	local rootdir=$1
	local livedir=$2
	local modules=${3:-} #additionnal kernel modules

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

	#tree and utilities
	rm -rf $rootdir/tmp/initrd-tree/
	mkdir -p $rootdir/tmp/initrd-tree/{bin,dev,etc/init.d,lib,mnt,proc,run,sbin,sys,usr/bin,usr/sbin}
	mkdir -p $rootdir/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 $rootdir/tmp/initrd-tree/usr/bin/

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

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

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

	#init scripts
	cat > $rootdir/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 $rootdir/tmp/initrd-tree/init
	rm -f $rootdir/tmp/initrd-tree/linuxrc
	local distro=$(get_distro_name $rootdir)
	cp $SCRIPT_DIR/{rcS,rcLive} $rootdir/tmp/initrd-tree/etc/init.d/
	cp $SCRIPT_DIR/rcLive-$distro.inc $rootdir/tmp/initrd-tree/etc/init.d/rcLive.inc
	chmod +x $rootdir/tmp/initrd-tree/etc/init.d/{rcS,rcLive}
	cat > $rootdir/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 $rootdir/tmp/initrd-tree
	find . | $CPIO -o -H newc | gzip -9c > ../initrd.gz
	cd - >/dev/null
	rm -rf $rootdir/tmp/initrd-tree
	mv $rootdir/tmp/initrd.gz $livedir/$LINOMAD/initrd.gz
}


function init_live() {
	local rootdir=$1
	local livedir=$2
	local modules=${3:-} #additionnal kernel modules
	local status=0

	local distro=$(get_distro_name $rootdir)
	source $SCRIPT_DIR/build-$distro.inc

	mkdir -p $livedir/efi/boot
	mkdir -p $livedir/$LINOMAD/modules

	#kernel
	echo "*** Copying kernel $LINOMAD/vmlinuz ..."
	cp -f $rootdir$KERNEL $livedir/$LINOMAD/vmlinuz

	#initrd
	echo "*** Generating $LINOMAD/initrd.gz ..."
	gen_initrd $rootdir $livedir $modules

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

	#grub.cfg
	if [ ! -f $livedir/efi/boot/grub.cfg ]; then
		echo "*** Generating GRUB configuration efi/boot/grub.cfg ..."
		#echo -e "insmod all_video\nset timeout=5\nset default=4" > $livedir/efi/boot/grub.cfg
		echo -e "terminal_output console\nset gfxmode=640x480\nset timeout=5\nset default=4" > $livedir/efi/boot/grub.cfg
		declare -A grubentry0=([entry]="Arabic" [locale]="ar_EG.UTF-8" [keymap]="us" [tz]="Asia/Riyadh")
		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/Kolkata")
		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")
		local syshome="sys=$LIVELABEL:$LINOMAD"
		if [ ! -z "$HOMELABEL" ]; then syshome="$syshome home=$HOMELABEL"; fi
		declare -n menuentry; for menuentry in ${!grubentry@}; do
			cat >> $livedir/efi/boot/grub.cfg << EOF
menuentry "${menuentry[entry]}" {
	linux /$LINOMAD/vmlinuz $syshome locale=${menuentry[locale]} keymap=${menuentry[keymap]} tz=${menuentry[tz]} hwc=localtime
	initrd /$LINOMAD/initrd.gz
}
EOF
		done
		cat >> $livedir/efi/boot/grub.cfg << EOF
menuentry " " { true }
menuentry "Busybox console" {
	linux /$LINOMAD/vmlinuz mode=busybox
	initrd /$LINOMAD/initrd.gz
}
EOF
	fi

	return $status
}


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

	mkdir -p $livedir/$LINOMAD/modules
	rm -f $livedir/$LINOMAD/modules/$module
	mksquashfs $rootdir $livedir/$LINOMAD/modules/$module $compoption -e tmp dev proc sys $livedir || return $FS_ERROR
}


function create_iso() {
	local livedir=$1
	local isofile=$2

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

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

	echo "*** Generating EFI bootable ISO $isofile ..."
	rm -f $isofile
	xorriso -outdev $isofile -volid "$LIVELABEL" -map $livedir / \
		-boot_image any cat_hidden=on -boot_image any platform_id=0x00 -boot_image grub bin_path=/bios.img \
		-boot_image grub grub2_mbr=$GRUB_PATH/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 $livedir/efi.img -boot_image any efi_path=--interval:appended_partition_2:all:: \
		-hide on /bios.img -- -rm_r /efi.img -- || { rm -f $livedir/efi.img $livedir/bios.img; return $FS_ERROR; }

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


function install_usb() {
	local livedir=$1
	local installdev=$2
	local wipe=${3:-}

	if [ ! -z "$wipe" ]; then
		echo "*** Partitioning $installdev ..."
		sgdisk -Z $installdev
		sgdisk -o -n 1:0:$EFI_SIZE -t 1:ef00 -n 2:0:0 $installdev || return $PART_ERROR
		partprobe $installdev; sleep 3
		local efipart="/dev/"$(basename /sys/block/${installdev#/dev}/${installdev#/dev}*1)
		local syspart="/dev/"$(basename /sys/block/${installdev#/dev}/${installdev#/dev}*2)
		echo "*** Formating EFI partition $efipart ..."
		mkfs.vfat $efipart || return $FS_ERROR
		echo "*** Formating system partition $syspart ..."
		mkfs.f2fs -f -l "$LIVELABEL" $syspart || return $FS_ERROR #-O ^has_journal -O encrypt
		sleep 3
	else
		local efipart=$(lsblk -o PATH,PARTTYPE -rn $installdev | sed -n '/ c12a7328-f81f-11d2-ba4b-00a0c93ec93b$/p' | cut -f1 -d' ')
		local syspart=$(lsblk -r -o PATH,LABEL $installdev | sed -n "/$LIVELABEL/p" | cut -f1 -d' ')
		echo "*** Checking EFI partition $efipart ..."
		fsck.vfat -a $efipart || exit $FS_ERROR
		echo "*** Checking system partition $syspart ..."
		fsck -f $syspart || exit $FS_ERROR
	fi

	echo "*** Copying GRUB boot loader, Linux kernel and InitRD ..."
	mkdir -p $INSTALL_PATH
	mount $efipart $INSTALL_PATH
	if [ -d $INSTALL_PATH/efi ]; then #FAT32 pseudo-trimming
		local size=$(du -sm $livedir/$LINOMAD/{initrd.gz,vmlinuz} $livedir/efi/ | awk '{s+=$1} END {print s}')
		local avail=$(df -m $INSTALL_PATH | sed -e 1d | awk '{ print $4}')
		if (( $size < $avail )); then
			mv $INSTALL_PATH/efi $INSTALL_PATH/efi.trim-$$
			mv $INSTALL_PATH/$LINOMAD $INSTALL_PATH/$LINOMAD.trim-$$
		else
			rm -rf $INSTALL_PATH/efi.trim-* $INSTALL_PATH/efi
			rm -rf $INSTALL_PATH/$LINOMAD.trim-$$ $INSTALL_PATH/$LINOMAD
		fi
	fi
	cp -r $livedir/efi $INSTALL_PATH/
	mkdir -p $INSTALL_PATH/$LINOMAD
	cp $livedir/$LINOMAD/{initrd.gz,vmlinuz} $INSTALL_PATH/$LINOMAD/
	umount $INSTALL_PATH
	
	echo "*** Copying SquashFS module files ..."
	mount $syspart $INSTALL_PATH
	mkdir -p $INSTALL_PATH/$LINOMAD
	rm -rf $INSTALL_PATH/$LINOMAD/modules
	cp -r $livedir/$LINOMAD/modules $INSTALL_PATH/$LINOMAD/
	umount $INSTALL_PATH
	rmdir $INSTALL_PATH
}


function install_system() {
	local rootdir=$1
	local syspart=$2

	local distro=$(get_distro_name $rootdir)
	source $SCRIPT_DIR/build-$distro.inc

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

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

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

	#fstab
	local uuid=$(lsblk -o UUID $syspart | sed 1d)
	echo "*** Adding fstab entry 'UUID=$uuid ($syspart) / ext4' ..."
	cat > $INSTALL_PATH/etc/fstab << EOF
#$syspart = $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 $INSTALL_PATH/etc/X11/xorg.conf.d/
	fi
	if [ -f /etc/dconf/db/local.d/00-keyboard ] && [ ! -z "$(which dconf 2>/dev/null)" ]; then #GNOME
		cp /etc/dconf/db/local.d/00-keyboard $INSTALL_PATH/etc/dconf/db/local.d/
		chroot $INSTALL_PATH dconf update
	fi
	if [ -f /etc/hypr/keymap.conf ]; then #Hyprland (Wayland)
		cp -f /etc/hypr/keymap.conf $INSTALL_PATH/etc/hypr
	fi

	if [ -n "$(ls -A /etc/NetworkManager/system-connections 2>/dev/null)" ]; then
		echo "*** Copying WiFi configuration ..."
		mkdir -p $INSTALL_PATH/etc/NetworkManager/system-connections
		cp -f /etc/NetworkManager/system-connections/* $INSTALL_PATH/etc/NetworkManager/system-connections/
		chmod -R go-rwx $INSTALL_PATH/etc/NetworkManager/system-connections
	fi

	umount $INSTALL_PATH; rmdir $INSTALL_PATH
}


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

	mkdir -p $INSTALL_PATH; mount $syspart $INSTALL_PATH
	mkdir -p $INSTALL_PATH/boot/grub
	local installdev="/dev/"$(ls -d /sys/block/*/$(basename $syspart) | cut -f4 -d'/')
	local efipart=$(lsblk -o PATH,PARTTYPE -rn $installdev | sed -n '/ c12a7328-f81f-11d2-ba4b-00a0c93ec93b$/p' | cut -f1 -d' ')
	local efibind=""
	
	if [ ! -z "$efipart" ]; then #GPT -> EFI boot
		echo "*** Checking $efipart (EFI partition) ..."
		fsck.vfat -a $efipart || exit $FS_ERROR
		efibind="sys/firmware/efi/efivars"
		mkdir -p $INSTALL_PATH/boot/efi
		mount $efipart $INSTALL_PATH/boot/efi
		for d in sys proc dev $efibind; do mount --bind /$d $INSTALL_PATH/$d; done
		echo "*** Installing EFI GRUB on $efipart ..."
		chroot $INSTALL_PATH grub-install --target=$GRUB_EFI_TARGET || status=$GRUB_ERROR
		for d in $efibind dev proc sys; do umount $INSTALL_PATH/$d; done
		umount $INSTALL_PATH/boot/efi
		local uuid=$(lsblk -o UUID $efipart | sed 1d)
		echo "*** Adding fstab entry 'UUID=$uuid ($efipart) /boot/efi vfat' ..."
		sed -e /\\/boot\\/efi/d -e/$uuid/d -i $INSTALL_PATH/etc/fstab
		echo -e "#$efipart = $uuid\nUUID=$uuid /boot/efi vfat defaults 1 1" >> $INSTALL_PATH/etc/fstab
	elif lsblk -o PTTYPE,PARTTYPE -rn $installdev | grep -q "^dos "; then #MBR -> BIOS (legacy) boot
		for d in sys proc dev; do mount --bind /$d $INSTALL_PATH/$d; done
		echo "*** Installing BIOS (legacy) GRUB on $installdev ..."
		chroot $INSTALL_PATH grub-install --force --target i386-pc $installdev || status=$GRUB_ERROR
		for d in dev proc sys; do umount $INSTALL_PATH/$d; done
	else
		echo "*** Error: $installdev is partitionned using GPT without EFI partition!"
	fi

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

	umount $INSTALL_PATH; rmdir $INSTALL_PATH
	return $status
}


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

	mkdir -p $INSTALL_PATH; mount $syspart $INSTALL_PATH
	mount $homepart $INSTALL_PATH/home

	local distro=$(get_distro_name $INSTALL_PATH)
	source $SCRIPT_DIR/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 $INSTALL_PATH/etc/fstab
	echo -e "#$homepart = $uuid\nUUID=$uuid /home $fs defaults 1 1" >> $INSTALL_PATH/etc/fstab

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

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

	umount $INSTALL_PATH/home
	umount $INSTALL_PATH; rmdir $INSTALL_PATH
}


function repair_system() {
	local syspart=$1

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

	mkdir -p $INSTALL_PATH; mount $syspart $INSTALL_PATH
	#fstab
	local uuid=$(lsblk -o UUID $syspart | sed 1d)
	local fs=$(mount | grep "$syspart" | cut -f5 -d' ')
	local tabfs=$(grep -E "^UUID=$uuid |^$syspart " $INSTALL_PATH/etc/fstab | awk '{print $3}')
	if [ "$fs" != "$tabfs" ]; then
		echo "*** Fixing fstab entry for root partition ($syspart) ..."
		sed -e '/ \/ /d' -i $INSTALL_PATH/etc/fstab
		echo -e "#$syspart = $uuid\nUUID=$uuid / $fs defaults 1 1" >> $INSTALL_PATH/etc/fstab
	fi

	#home partition filesystem check
	local homepart=$(awk '$2 == "/home" {print $1}' $INSTALL_PATH/etc/fstab)
	if [ ! -z "$homepart" ]; then
		fsck -f $homepart || { umount $INSTALL_PATH; rmdir $INSTALL_PATH; return $FS_ERROR; }
	else
		echo "*** Notice: No /home partition configured in /etc/fstab"
	fi

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

	umount $INSTALL_PATH; rmdir $INSTALL_PATH
}


action=${1:-}
case $action in
"--init")
	rootdir=${2:-}
	livedir=${3:-}
	modules=${4:-}
	if [ ! -d "$rootdir" ] || [ -z "$livedir" ]; then
		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
	else
		init_live $rootdir $livedir $modules
		exit $?
	fi
	;;

"--module")
	rootdir=${2:-}
	livedir=${3:-}
	module=${4:-}
	compression=${5:--zstd}
	compression=${compression#-}
	if ! echo $SQUASHFS_COMPRESSION | grep -q "$compression"; then
		echo "error: invalid compression option '$compression'"
		exit $CMD_ERROR
	fi
	if [ ! -d "$rootdir" ] || [ ! -d "$livedir" ] || [ -z "$module" ]; then
		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
	else
		add_module $rootdir $livedir $module $compression
		exit $?
	fi
	;;

"--iso")
	livedir=${2:-}
	isofile=${3:-}
	if [ ! -d "$livedir" ] || [ -z "$isofile" ] || [ ! -d "$(dirname $isofile)" ]; then
		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
	else
		create_iso $livedir $isofile
		exit $?
	fi
	;;

"--usb")
	livedir=${2:-}
	installdev=${3:-}
	wipe=${4:-}
	if [ ! -d "$livedir" ] || [ -z "$installdev" ] || [ ! -d /sys/block/$(basename $installdev) ]; then
		echo -e "Copies live system on USB device.\n"
		echo "usage: $(basename $0) --usb live_dir device [wipe]"
		echo "example: $(basename $0) --usb live /dev/sdx"
		exit $CMD_ERROR
	fi
	livesize=$(du -sm "$livedir" | awk '{ print $1 }')
	efipart=$(lsblk -o PATH,PARTTYPE -rn $installdev | sed -n '/ c12a7328-f81f-11d2-ba4b-00a0c93ec93b$/p' | cut -f1 -d' ') || efipart=''
	syspart=$(lsblk -r -o PATH,LABEL $installdev | sed -n "/$LIVELABEL/p" | cut -f1 -d' ') || syspart=''
	if ( [ -z "$efipart" ] || [ -z "$syspart" ] ) && [ "$wipe" != "wipe" ]; then
		echo "error: missing EFI or system partition:"
		cat << EOF
 Partition |  Type  | Format |  Label
 --------- | ------ | ------ | -------
 EFI       | 0xef00 | vfat   |
 System    | 0x8300 | f2fs   | $LIVELABEL
EOF
		echo "enforce: $0 --usb $livedir $installdev wipe"
		exit $WIPE_ERROR
	fi
	if [ -z "$syspart" ]; then
		mediasize=$(cat /sys/block/$(basename $installdev)/size)
	else
		mediasize=$(cat /sys/block/$(basename $installdev)/$(basename $syspart)/size)
	fi
	let mediasize=mediasize/2048
	if (( $livesize > $mediasize )); then 
		echo "error: insufficient space: $livesize MB needed / $mediasize MB available"
		exit $SIZE_ERROR
	fi
	if [ "$wipe" == "wipe" ]; then
		echo -n "USB copy will wipe everything on $installdev; continue [y,N]? "; read confirm
		if [ "$confirm" != "y" ]; then exit; fi
	fi
	echo "install_usb $livedir $installdev"
	install_usb $livedir $installdev $wipe
	exit $?
	;;

"--install")
	rootdir=${2:-}
	syspart=${3:-}
	if [ ! -d "$rootdir" ] || [ -z "$syspart" ] || [ ! -d /sys/block/*/$(basename $syspart) ]; then
		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
	else
		echo -n "System install will wipe everything on $syspart; continue [y,N]? "; read confirm
		if [ "$confirm" != "y" ]; then exit; fi
		install_system $rootdir $syspart && setup_grub $syspart
		exit $?
	fi
	;;

"--repair")
	syspart=${2:-}
	if [ -z "$syspart" ] || [ ! -d /sys/block/*/$(basename $syspart) ]; then
		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
	else
		repair_system $syspart && setup_grub $syspart
		exit $?
	fi
	;;

"--home")
	syspart=${2:-}
	homepart=${3:-}
	login=${4:-}
	password=${5:-}
	if [ -z "$syspart" ] || [ ! -d /sys/block/*/$(basename $syspart) ] ||
	   [ -z "$homepart" ] || [ ! -d /sys/block/*/$(basename $homepart) ] ||
	   [ "$homepart" == "$syspart" ] || [ -z "$login" ] || [ -z "$password" ] ; then
		echo -e "Setup /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
	else
		echo "*** Checking '$syspart' (root partition) ..."
		fsck -f $syspart || exit $FS_ERROR
		echo "*** Checking '$homepart' (home partition) ..."
		fsck -f $homepart || exit $FS_ERROR
		setup_home $syspart $homepart $login "$password"
		#exit $? (no 'return $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) --usb live_dir device [wipe]"
	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 "- LINOMAD ($LINOMAD): live-system directory name"
	echo "- HOMELABEL: alternative data filesystem label"
	exit $CMD_ERROR
	;;
esac
