#!/bin/bash
# SPDX-License-Identifier: GPL-3.0-or-later

###################
### Main script ###
###################

# Safety first
set -o errexit
set -o noglob
set -o nounset
set -o errtrace

# Full path to this script
#
# shellcheck disable=SC2155
readonly scriptname="$(realpath -- "$0")"

# Short name of this program
readonly progname="${scriptname##*/}"

# Location of sources: requires on-site launch capability
if [ "$scriptname" = "/usr/bin/$progname" ]; then
	readonly libdir="/usr/libexec/$progname"
else
	readonly libdir="${scriptname%/*}"/sysrest
fi

# Bootstrap
. "$libdir"/common.sh
. "$libdir"/restore/common.sh
. "$libdir"/restore/defaults.sh
. "$libdir"/restore/internal.sh
#
# shellcheck disable=SC1090
[ ! -s "/etc/$progname"/restore.conf ] ||
	. "/etc/$progname"/restore.conf
#
. "$libdir"/restore/parser.sh
. "$libdir"/restore/target.sh
. "$libdir"/restore/restore.sh

# Catch all unexpected errors
trap 'unexpected_error "${BASH_SOURCE[0]##*/}" "$LINENO"' ERR

# Parsing command line arguments
parse_cmdline "$@"

# Determining the target platform and its default parameters
detect_hw_platform

# This program requires root privileges to run
check_superuser

# Checking for the necessary binaries
#
# shellcheck disable=SC2046
check_requires $required_tools $(get_proto_requires)

# Enabling logging and debugging
. "$libdir"/logger.sh
setup_logger "$@"

# Start checking the backup
if [ -n "$use_backup" ]; then
	. "$libdir"/restore/backup.sh
	. "$libdir"/profile/hardware.sh

	set_recovery_options
fi

# Show the banner
[ -n "$quiet" ] ||
	welcome_text

# Checking files and metadata of the backup
[ -z "$use_backup" ] ||
	check_backup_files
unset use_backup

# Determining the list of protected disks that cannot be targets
protect_boot_devices

# Checking the validity of the specified target disk
if [ -z "$target" ] || [ "$action" = scandisk ]; then
	# the target disk is not relevant for the disk scan operation
	[ -z "$target" ] ||
		warn "The target will be ignored with the --scan-only action."
	target=

	# autodetecting the target disk or selecting it manually
	search_target_drive
else
	dummy="$target"
	is_blkdev "$target" ||
		fatal "Target device not found: '%s'." "$target"
	get_whole_disk dummy "$target" && [ "$dummy" = "$target" ] ||
		fatal "Target device must be a whole disk: '%s'." "$target"
	! in_array "$target" $protected_devices ||
		fatal "The target is on the list of write-protected devices."
	unset dummy

	# a separator between the device name of
	# the whole disk and the partition number
	#
	case "$target" in
	*[0-9])	ppartsep="p";;
	*)	ppartsep="";;
	esac
fi

# No longer needed
unset target_model
unset choose_target
unset required_tools
unset min_target_size
unset max_target_size
unset protected_devices
unset protected_mpoints

# Defining the final deployment configuration
deployment_config

# Creating a disk partitioning scheme
track_hook diskprep_make_scheme

# Defining partition devices
track_hook diskprep_set_devices

# The root partition is required in any case
if [ -z "$rootpart" ]; then
	fatal "Internal error: root partition required."
fi

# Setting the future hostname
setup_hostname

# Intermediate diagnostics
[ -z "$SYSREST_DEBUG" ] || [ -z "$logfile" ] ||
	show_diag_msg --debug >&2
[ -z "$json_output" ] ||
	show_diag_json
[ -n "$quiet" ] || [ -n "$json_output" ] ||
	show_diag_msg
[ -z "$intentions" ] ||
	exit 0
stop_other_instances

# Ignoring interrupts
trap : INT TERM QUIT HUP USR1 USR2

# Applying a new disk partitioning scheme
track_hook diskprep_apply_scheme

# Formatting partitions
format_all_targets

# No longer needed
unset prepsize
unset esp_size
unset bbp_size
unset swapsize
unset bootsize
unset rootsize

# Recovering the root partition
if [ -n "$rootpart" ]; then
	msg "Restoring the / (root) partition..."

	[ -d "$destdir" ] ||
		run mkdir -p $verbose -- "$destdir"
	run chmod $quiet $verbose -- 0755 "$destdir"

	track_hook diskprep_mount_dest /

	# Move the contents of the /boot directory to another partition
	if [ -n "$bootpart" ] && ! file_exists boot.tgz; then
		run mkdir -m 0700 $verbose -- "$destdir"/boot
	fi

	unpack_image root

	# Ensure that the access rights to the root directory are correct
	run chmod $quiet $verbose -- 0755 "$destdir"
fi

# Recovering the originally separate /boot
if [ -n "$bootpart" ] && file_exists boot.tgz; then
	msg "Restoring the /boot partition..."
	[ -d "$destdir"/boot ] ||
		run mkdir -m 0700 $verbose -- "$destdir"/boot
	[ -z "$bootpart" ] ||
		track_hook diskprep_mount_dest /boot
	unpack_image boot /boot
fi

# Recovering ESP
if [ -n "$esp_part" ] || file_exists esp.tgz; then
	msg "Restoring the /boot/efi (ESP) partition..."
	[ -d "$destdir"/boot/efi ] ||
		run mkdir -p -m 0755 $verbose -- "$destdir"/boot/efi
	[ -z "$esp_part" ] ||
		track_hook diskprep_mount_dest /boot/efi
	! file_exists esp.tgz ||
		unpack_image esp /boot/efi
	run mkdir -p -m 0755 $verbose -- "$destdir"/boot/efi/EFI
fi

# Recovering the user data partition
if [ -n "$datapart" ] || file_exists home.tgz || file_exists var.tgz; then
	msg "Restoring the %s (user data) partition..." "$datapart_mp"

	if [ "$datapart_mp" = /home ] || file_exists home.tgz; then
		run mkdir -p -m 0755 $verbose -- "$destdir"/home
	elif [ "$datapart_mp" = /var ] || file_exists var.tgz; then
		run mkdir -p -m 0755 $verbose -- "$destdir"/var
	fi

	if [ -n "$datapart" ]; then
		track_hook diskprep_mount_dest "$datapart_mp"
	fi

	if [ "$action" != sysrest ]; then
		if file_exists home.tgz; then
			unpack_image home "$datapart_mp"
		elif file_exists var.tgz; then
			unpack_image var "$datapart_mp"
		fi
	fi
fi

# The partitioner-specific finalization of the rootfs unpacking
track_hook diskprep_post_unpack

# Creating a SWAP-file, if defined
create_swap_file

# Ensure uniqueness
msg "Personification..."
[ "$template" = "$computer" ] || [ ! -f "$destdir"/etc/sysconfig/network ] ||
	run sed -i "s,$template,$computer," "$destdir"/etc/sysconfig/network
printf "%s\n" "$computer" >"$destdir"/etc/hostname
#
if [ -n "$unique_clone" ]; then
	run uuidgen |tr -d '-' >"$destdir"/etc/machine-id
	[ ! -d "$destdir"/var/lib/dbus ] ||
		run cp -Lf $verbose -- "$destdir"/etc/machine-id \
					"$destdir"/var/lib/dbus/machine-id
	if [ -s "$workdir"/RNDSEED ]; then
		dummy="${destdir}$(run head -n1 -- "$workdir"/RNDSEED)"
		if [ -d "${dummy%/*}" ]; then
			run head -c512 -- /dev/urandom >"$dummy"
			run chmod $quiet $verbose -- 0600 "$dummy"
		fi
		unset dummy
	fi
fi

# Recreating the /etc/fstab file
if [ -z "$keep_fstab" ]; then
	mkfstab >"$destdir"/etc/fstab
	run chmod $quiet $verbose -- 0644 "$destdir"/etc/fstab
	fdump "$destdir"/etc/fstab
fi

# Recreating GRUB early boot configuration
if [ "$safe_uefi_boot" != 1 ]; then
	efidir="$destdir/boot/efi/EFI/$bootldr_id"
else
	run rm -rf $verbose -- "$destdir/boot/efi/EFI/$bootldr_id"
	efidir="$destdir/boot/efi/EFI/BOOT"
fi
if [ -z "$keep_grub_cfg" ] && [ -d "$efidir" ]; then
	if [ -n "$bootpart" ]; then
		dummy=
	elif [ "$partitioner" = timeshift ]; then
		dummy=/@/boot
	else
		dummy=/boot
	fi
	cat >"$efidir"/grub.cfg <<-EOF
	search --root-dev-only --set=root --fs-uuid $grubuuid 
	set prefix=(\$root)'$dummy/grub'
	configfile \$prefix/grub.cfg
	EOF
	run chmod $quiet $verbose -- 0644 "$efidir"/grub.cfg
	fdump "$efidir"/grub.cfg
	unset dummy
fi
unset efidir

# Defining the boot parameters of the target system
[ -n "$bootargs" ] || [ ! -s "$destdir"/etc/sysconfig/grub2 ] || {
	bootargs="$(read_kernel_args "$destdir"/etc/sysconfig/grub2)"
	e2kadd_bootargs=
}
[ -z "$swappart" ] || [ -n "$no_hibernate" ] ||
	bootargs="resume=UUID=$swapuuid $bootargs"
[ -z "$resume_offset" ] || [ -z "$swapfile_hibernation" ] ||
	bootargs="resume=UUID=$swapuuid resume_offset=$resume_offset $bootargs"
[ "$platform" != elbrus ] || [ -z "$e2kadd_bootargs" ] ||
	bootargs="$e2kadd_bootargs $bootargs"
[ -z "$oem_setup_steps" ] && [ ! -s "$destdir"/etc/alterator-setup/steps ] ||
	bootargs="$bootargs systemd.unit=setup.target"

# Changing GRUB device names
f="$destdir/etc/sysconfig/grub2"
if [ -s "$f" ]; then
	k="GRUB_CMDLINE_LINUX_DEFAULT"
	run sed -i -E "s|^$k=.*$|$k='$bootargs '|" "$f"
	k="GRUB_AUTOUPDATE_DEVICE"
	v="$(run mountpoint -x -- "$target")"
	v="$(run grep -s "S:disk/by-id/" "/run/udev/data/b$v" |
					head -n1 |cut -f2- -d:)"
	run sed -i -E "s|^#?$k=.*$|$k='/dev/$v '|" "$f"
	run chmod $quiet $verbose -- 0644 "$f"
	fdump "$f"
	unset v k
fi
unset f

# Renaming the only wired network interface
if [ -n "$oldifname" ] && [ -d "$destdir/etc/net/ifaces/$oldifname" ] &&
	[ -n "$newifname" ] && [ "$newifname" != "$oldifname" ] &&
	[ ! -d "$destdir/etc/net/ifaces/$newifname" ]
then
	run mv -f $verbose -- "$destdir/etc/net/ifaces/$oldifname" \
				"$destdir/etc/net/ifaces/$newifname"
	[ ! -s "$destdir/etc/net/ifaces/$newifname"/options ] ||
		fdump "$destdir/etc/net/ifaces/$newifname"/options
fi

# Creating a file with the version of the
# deployed system and the deployment date
#
if [ -n "$release_file" ]; then
	printf "%s %s\n" "$release" "$(env LC_TIME=C date +'%F')" \
					>"${destdir}${release_file}"
fi

# Preparing a temporary directory for a chroot
if [ -n "$use_chroot" ]; then
	. "$libdir"/restore/pchroot.sh

	prepare_chroot
fi

# No longer needed
unset release_file
unset oldifname
unset newifname
unset bootargs
unset template

# Clearing all entries from NVRAM: UEFI boot mode only
if [ -z "$no_nvram" ] && [ -n "$uefiboot" ]; then
	[ -n "$no_clear_nvram" ] || clear_nvram
fi

# One-time chroot into the target system
[ -z "$use_chroot" ] || invoke_chroot

# Ensure reliable loading
if [ "$safe_uefi_boot" = 2 ] &&
   [ -s "$destdir/boot/efi/EFI/$bootldr_id"/grub.cfg ]
then
	if [ -d "$destdir"/boot/efi/EFI/BOOT ]
	then
		run cp -f $verbose -- \
			"$destdir/boot/efi/EFI/$bootldr_id"/grub.cfg \
			"$destdir"/boot/efi/EFI/BOOT/
		run chmod $quiet $verbose -- 0644 \
			"$destdir"/boot/efi/EFI/BOOT/grub.cfg
		fdump "$destdir"/boot/efi/EFI/BOOT/grub.cfg
	else
		run cp -aRf $verbose -- \
			"$destdir/boot/efi/EFI/$bootldr_id" \
			"$destdir"/boot/efi/EFI/BOOT
		dummy="BOOT${efi_suffix@U}"
		#
		if [ -s "$destdir/boot/efi/EFI/BOOT/$dummy" ]; then
			: # Do nothing at this point
		elif [ -s "$destdir/boot/efi/EFI/BOOT/shim$efi_suffix" ]; then
			run mv -f $verbose -- \
				"$destdir/boot/efi/EFI/BOOT/shim$efi_suffix" \
				"$destdir/boot/efi/EFI/BOOT/$dummy"
		elif [ -s "$destdir/boot/efi/EFI/BOOT/grub$efi_suffix" ]; then
			run mv -f $verbose -- \
				"$destdir/boot/efi/EFI/BOOT/grub$efi_suffix" \
				"$destdir/boot/efi/EFI/BOOT/$dummy"
		fi
		#
		unset dummy
	fi
fi

# Creating a boot entry in NVRAM: UEFI boot mode only
if [ -z "$no_nvram" ] && [ -n "$uefiboot" ]; then
	[ "$platform" = ppc64le ] || record_nvram
fi

# The partitioner-specific finalization before unmounting partitions
track_hook diskprep_before_unmount

# Copying the log file to the target system
if [ -n "$SYSREST_DEBUG" ] && [ -n "$logfile" ]; then
	if [ -d "$destdir"/var/log ]; then
		dummy=/var/log
	elif [ -d "$destdir"/root/.install-log ]; then
		dummy=/root/.install-log
	else
		dummy=/root
	fi

	( echo
	  [ -n "$no_nvram" ] || [ -z "$uefiboot" ] || {
		run efibootmgr ||:
		echo
	  }
	  run lsblk -f -- "$target" ||:
	  echo
	  run sfdisk -l -- "$target" ||:
	) >&2

	run cp -Lf $verbose -- "$logfile" "$destdir$dummy"/
	unset dummy
fi

# Unmounting all partitions of the target disk
{ [ -z "$datapart" ] ||
	umount -R $quiet $verbose -- "${destdir}${datapart_mp}" ||:
  [ -z "$esp_part" ] ||
	umount -R $quiet $verbose -- "$destdir"/boot/efi ||:
  [ -z "$bootpart" ] ||
	umount -R $quiet $verbose -- "$destdir"/boot ||:
  umount -R $quiet $verbose -- "$destdir" ||
	umount -Rfl $quiet $verbose -- "$destdir" ||:
} 2>/dev/null

# The partitioner-specific finalization of system restore
track_hook diskprep_finalize

# No longer needed
unset datapart_mp
unset grubuuid
unset esp_uuid
unset bootuuid
unset swapuuid
unset rootuuid
unset datauuid
unset preppart
unset esp_part
unset bbp_part
unset bootpart
unset swappart
unset rootpart
unset datapart

# Creating a file to turn off or reboot the computer and making
# it executable: this is necessary to unmount the /mnt/autorun
# partition from which the autorun script is launched
#
if [ -n "$finalact" ]; then
	cat >/tmp/finish.sh <<-EOF
	#!/bin/sh
	umount -R $quiet $verbose -- "\$1" 2>/dev/null ||
	    umount -Rfl $quiet $verbose -- "\$1"
	$finalact
	exit 1
	EOF

	if run mountpoint -q /tmp; then
		will_run mount -o remount,exec $verbose /tmp
		mount -o remount,exec $verbose /tmp 2>/dev/null ||:
	fi

	run chmod $quiet $verbose -- 0755 /tmp/finish.sh
fi

# Final cleaning and shutdown or restart the computer
#
if [ -z "$quiet" ]; then
	echo -e "$CLR_OK"
	echo "Computer '$computer' restored successfully!"
	echo "Enjoy! ;-)"
	echo -e "$CLR_NORM"
fi
#
if [ -n "$SYSREST_DEBUG" ]; then
	[ -z "$quiet" ] ||
		exec 1>&3 3>&-
	quiet=
	[ -n "$no_nvram" ] || [ -z "$uefiboot" ] || {
		run efibootmgr ||:
		echo
	}
	run lsblk -f -- "$target" ||:
	echo
	run sfdisk -l -- "$target" ||:
	echo
	dummy="Press Ctrl-D to exit the debug shell..."
	echo -e "${CLR_WARN}${dummy}${CLR_NORM}"
	PS1="(${CLR_LC2}DEBUG${CLR_NORM}) # " /bin/bash -i 2>&1
fi
#
if [ -n "$finalact" ]; then
	exec /tmp/finish.sh "$(pwd)"
	$finalact
	unexpected_error "$progname" "$LINENO"
fi

