#!/bin/bash
#
# Copyright (C) MOXA Inc. All rights reserved.
# This software is distributed under the terms of the MOXA SOFTWARE NOTICE.
# See the file MOXA-SOFTWARE-NOTICE for details.
#
# Authors:
# 	2021	Wes Huang	<Wes.Huang@moxa.com>

if [ -f "${GENERAL_VARIABLES}" ]; then
	source "${GENERAL_VARIABLES}"
fi
if [ -f "${GENERAL_FUNCTIONS}" ]; then
	source "${GENERAL_FUNCTIONS}"
fi

set_backup_compress_flag() {
	local flag="${1}"

	_log_msg "debug" "${0}, ${FUNCNAME[0]}"
	_log_msg "debug" "flag=${flag}, MX_SYSTEM_MGMT_BACKUP_COMPRESS_FLAG_FILE=${MX_SYSTEM_MGMT_BACKUP_COMPRESS_FLAG_FILE}"

	echo "${flag}" >"${MX_SYSTEM_MGMT_BACKUP_COMPRESS_FLAG_FILE}"
	sync

	return 0
}

backup_create() {
	local create_time="${1}"
	local ret_val

	determine_backup_file_name "create"

	_log_msg "debug" "${0}, ${FUNCNAME[0]}"
	_log_msg "debug" "create_time=${create_time}, BACKUP_FILE=${BACKUP_FILE}, BACKUP_FILE_NAME=${BACKUP_FILE_NAME}"

	if [ "${MSM_OPTION_SIZE}" == "y" ]; then
		_calculate_transferred_size "backup" "y" "y"
		return 0
	fi

	if [ "${MSM_OPTION_MODE}" == "hot" ]; then
		_log_msg "info" "It is recommended to use cold creation mode 'mx-system-mgmt backup create --cold'"
	fi

	_check_info_file "backup"
	ret_val="${?}"

	if [ "${ret_val}" == 0 ]; then
		_show_info "backup"
		_log_msg "info" "A backup already exist. This will overwrite the existing backup."
		_question "${MSM_FLAG_YES}" "Would you like to continue? (y/N)"
		_remove_info "backup"

		# clean the files that under backup directory
		rm -rf "${BACKUP_DIR:?}"/*
	fi

	# evaluate the available space and estimated use size
	_calculate_transferred_size "backup" "${MSM_FLAG_YES}" "n"

	create_backup_file
	# start creating backup file
	if [ "${MSM_OPTION_MODE}" == "cold" ]; then
		_set_cold_creation_flag "backup"

		if [ "${MSM_OPTION_COMPRESS}" == "y" ]; then
			set_backup_compress_flag "y"
		else
			set_backup_compress_flag "n"
		fi

		_log_msg "info" "Please reboot the device to continue the process."
	else
		_create_info "backup" "${create_time}" "${BACKUP_DIR}" "${BACKUP_FILE_NAME}"
		_show_info "backup"
		_log_msg "info" "The backup has been created successfully under: ${BACKUP_DIR}"
	fi
}

create_backup_file() {
	local tar_option="--warning='no-file-ignored' --xattrs --xattrs-include='*' -cpf - -C / \"${WORKING_DIR##/}\" \"${SNAPSHOT_DIR##/}\" \"${DEFAULT_DIR##/}\" \"${ROOTFS_SQAUSHFS##/}\""
	local ret_val

	_log_msg "debug" "${0}, ${FUNCNAME[0]}"
	_log_msg "debug" "MSM_OPTION_MODE=${MSM_OPTION_MODE}, BACKUP_DIR=${BACKUP_DIR}, BACKUP_FILE=${BACKUP_FILE}"
	_log_msg "debug" "WORKING_DIR=${WORKING_DIR}, SNAPSHOT_DIR=${SNAPSHOT_DIR}, DEFAULT_DIR=${DEFAULT_DIR}, ROOTFS_SQAUSHFS=${ROOTFS_SQAUSHFS}, ROOTFS_SQAUSHFS_HASH_SIGNED=${ROOTFS_SQAUSHFS_HASH_SIGNED}"

	# sync p1 latest boot file to p3 working/boot.
	_log_msg "info" "Synchronize boot files..."
	_rsync "${BOOT_DIR}" "${WORKING_DIR}/boot"

	if [ "${MSM_OPTION_MODE}" != "cold" ]; then
		# create backup file
		_log_msg "info" "Start creating backup file..."
		mkdir -p "${BACKUP_DIR}"
		if [ ! -f "${ROOTFS_SQAUSHFS_HASH_SIGNED}" ]; then
			_log_msg "warn" "There is no ${ROOTFS_SQAUSHFS_HASH_SIGNED} file, the system environment is insecure."
		else
			tar_option="${tar_option} \"${ROOTFS_SQAUSHFS_HASH_SIGNED##/}\""
		fi
		if [ "${MSM_OPTION_COMPRESS}" == "y" ]; then
			tar_option="${tar_option} --gzip"
		fi
		_log_msg "debug" "tar_option=${tar_option}"
		eval tar "${tar_option}" | (pv -p --timer --rate --bytes >"${BACKUP_FILE}")
		ret_val="${PIPESTATUS[0]}"
		if [ "${ret_val}" != "0" ]; then
			_log_msg "error" "There is something wrong when creating backup file..."
			exit "${_ERR_CREATE_TAR}"
		fi
	fi
	sync
}

restore_backup_file() {
	local tmp_backup_restore_dir="${P3_DIR}/backup_restore"
	local ret_val
	local tar_option="-xp"

	_log_msg "debug" "${0}, ${FUNCNAME[0]}"
	_log_msg "debug" "BACKUP_FILE=${BACKUP_FILE}, tmp_backup_restore_dir=${tmp_backup_restore_dir}"

	# extract backup file.
	_log_msg "info" "Start using the backup file to restore the system..."
	mkdir -p "${tmp_backup_restore_dir}"

	# shellcheck disable=SC1090
	source "${BACKUP_INFO_FILE}"
	if [ "${COMPRESSED}" == "y" ]; then
		tar_option="-xpz"
	fi

	pv -p --timer --rate --bytes "${BACKUP_FILE}" | tar --xattrs --xattrs-include='*' ${tar_option} -C "${tmp_backup_restore_dir}"
	ret_val="${?}"
	if [ "${ret_val}" != "0" ]; then
		_log_msg "error" "There is something wrong when creating backup file..."
		exit "${_ERR_EXTRACT_TAR}"
	fi

	# replace snapshot and default.
	_log_msg "debug" "replace snapshot and default."
	_log_msg "debug" "SNAPSHOT_DIR=${SNAPSHOT_DIR}, DEFAULT_DIR=${DEFAULT_DIR}"

	rm -rf "${SNAPSHOT_DIR}"
	mv "${tmp_backup_restore_dir}${SNAPSHOT_DIR}" "${SNAPSHOT_DIR}"
	rm -rf "${DEFAULT_DIR}"
	mv "${tmp_backup_restore_dir}${DEFAULT_DIR}" "${DEFAULT_DIR}"

	# setup backup_restore folder and rootfs sqaushfs file.
	_log_msg "debug" "setup backup_restore folder and rootfs sqaushfs file."
	_log_msg "debug" "WORKING_DIR=${WORKING_DIR}, RESTORE_DIR=${RESTORE_DIR}, ROOTFS_SQAUSHFS=${ROOTFS_SQAUSHFS}, ROOTFS_SQAUSHFS_HASH_SIGNED=${ROOTFS_SQAUSHFS_HASH_SIGNED}"

	rm -rf "${RESTORE_DIR}"
	mv "${tmp_backup_restore_dir}${WORKING_DIR}" "${RESTORE_DIR}"
	mv "${tmp_backup_restore_dir}${ROOTFS_SQAUSHFS}" "${RESTORE_DIR}"
	if [ -f "${tmp_backup_restore_dir}${ROOTFS_SQAUSHFS_HASH_SIGNED}" ]; then
		mv "${tmp_backup_restore_dir}${ROOTFS_SQAUSHFS_HASH_SIGNED}" "${RESTORE_DIR}"
	else
		_log_msg "warn" "There is no ${ROOTFS_SQAUSHFS_HASH_SIGNED} file, the system environment is insecure."
	fi

	_log_msg "debug" "restore boot file and bootloader."
	_log_msg "debug" "WORKING_BOOT_DIR=${WORKING_BOOT_DIR}, BOOTLOADER_WORKING_DEVICE=${BOOTLOADER_WORKING_DEVICE}"

	_log_msg "info" "Synchronize boot files..."
	_rsync "${RESTORE_DIR}/boot" "${WORKING_BOOT_DIR}"
	sync
}

check_backup_file_integrity() {
	local backup_file_sha

	_log_msg "info" "Start verifying backup file, please wait..."

	if [[ "${SYS_ARCH}" == "armv7l" ]]; then
		backup_file_sha="$(sha256sum "${BACKUP_FILE}" | awk '{print $1}')"
	elif [[ "${SYS_ARCH}" == "x86_64" ]] || [[ "${SYS_ARCH}" == "aarch64" ]]; then
		backup_file_sha="$(sha512sum "${BACKUP_FILE}" | awk '{print $1}')"
	fi

	# shellcheck disable=SC1090
	source "${BACKUP_INFO_FILE}"

	# shellcheck disable=SC2153
	if [ "${SHA_VAL}" == "${backup_file_sha}" ]; then
		_log_msg "info" "Verified OK!"
	else
		_log_msg "error" "The ${BACKUP_FILE} file is incorrect."
		exit "${_ERR_HASH_VALUE}"
	fi
}

check_model_name() {
	# shellcheck disable=SC1090
	source "${BACKUP_INFO_FILE}"

	# shellcheck disable=SC2153
	if [ "${MODEL_NAME}" != "${DEVICE_MODEL_NAME}" ]; then
		_log_msg "warn" "This backup file was created from ${MODEL_NAME}. The device is ${DEVICE_MODEL_NAME}. The model name is different."
		exit "${_ERR_MODEL_NAME}"
	fi
}

backup_restore() {
	local state
	local ret_val

	_log_msg "debug" "${0}, ${FUNCNAME[0]}"

	_check_info_file "backup" && _show_info "backup" || exit ${?}
	determine_backup_file_name "restore"
	_check_min_req_tool_ver "backup"

	# if replica has been started, the system cannot perform restore related behaviors.
	state=$(mx-system-mgmt --system-failback state --value)
	if [ "${state}" != "${_STATE_DISABLED}" ]; then
		_log_msg "info" "System failback has been enabled and the replica has been created on your system. Continue with restore will disable system failback and remove replica."
		_question "${MSM_FLAG_YES}" "Would you like to continue? (y/N)"
		mx-system-mgmt --system-failback disable -y
	fi
	check_model_name
	check_backup_file_integrity
	_calculate_transferred_size "backup_restore" "${MSM_FLAG_YES}" "n"
	_check_info_file "snapshot"
	ret_val="${?}"

	if [ "${ret_val}" == 0 ]; then
		_show_info "snapshot"
		_log_msg "info" "This will delete the existing snapshot."
		_question "${MSM_FLAG_YES}" "Do you want to continue? (y/N)"
		mx-system-mgmt --snapshot delete -y
	fi

	_log_msg "info" "To restore the backup file will overwrite current system and factory default system."
	_question "${MSM_FLAG_YES}" "Do you want to continue? (y/N)"
	restore_backup_file
	_set_flag "backup_restore"
	_log_msg "info" "System has been restored successfully. Reboot is required to take effect."
}

backup_delete() {
	_log_msg "debug" "${0}, ${FUNCNAME[0]}"

	_check_info_file "backup" && _show_info "backup" || exit ${?}
	_question "${MSM_FLAG_YES}" "Do you want to continue? (y/N)"

	# clean the files that under backup directory
	rm -rf "${BACKUP_DIR:?}"/*

	# sync and result
	sync
	_log_msg "info" "The backup has been deleted successfully."
}

backup_check_option_input_directory() {
	_log_msg "debug" "${0}, ${FUNCNAME[0]}"

	if [ "${MSM_OPTION_DIR}" == "y" ]; then
		if [ -d "${MSM_OPTION_DIR_PATH}" ]; then
			# circular check (If the target directory is under system's overlayfs, it will be a circular copy.)
			if df -m "${MSM_OPTION_DIR_PATH}" | awk '{print $1}' | grep -q overlay; then
				_log_msg "error" "The ${MSM_OPTION_DIR_PATH} directory is under root system, a circular copy will occur."
				exit "${_ERR_DIR}"
			fi
			_log_msg "info" "Set ${MSM_OPTION_DIR_PATH} as backup directory."
			BACKUP_DIR="${MSM_OPTION_DIR_PATH}"
			BACKUP_INFO_FILE="${BACKUP_DIR}/info"
			return 0
		else
			_log_msg "error" "${MSM_OPTION_DIR_PATH} directory is not exist."
			exit "${_ERR_DIR}"
		fi
	elif [ "${MSM_OPTION_SIZE}" == "y" ]; then
		return 0
	else
		_log_msg "info" "Default backup directory: ${BACKUP_DIR}"
		return 0
	fi
}

determine_backup_file_name() {
	local sub_command="${1}"

	if [ "${sub_command}" == "create" ]; then
		if [ "${MSM_OPTION_COMPRESS}" == "y" ]; then
			BACKUP_FILE_NAME="backup.tar.gz"
		else
			BACKUP_FILE_NAME="backup.tar"
		fi
	else
		# shellcheck disable=SC1090
		source "${BACKUP_INFO_FILE}"
		if [ "${COMPRESSED}" == "y" ]; then
			BACKUP_FILE_NAME="backup.tar.gz"
		else
			BACKUP_FILE_NAME="backup.tar"
		fi
	fi
	BACKUP_FILE="${BACKUP_DIR}/${BACKUP_FILE_NAME}"
}

main() {
	local sub_command="${1}"
	local create_time
	local ret_val

	create_time="$(date +%s)"

	_log_msg "debug" "${0}, ${FUNCNAME[0]}"
	_log_msg "debug" "sub_command=${sub_command}, create_time=${create_time}"
	_log_msg "debug" "MSM_OPTION_MODE=${MSM_OPTION_MODE}, MSM_OPTION_SIZE=${MSM_OPTION_SIZE}, MSM_FLAG_YES=${MSM_FLAG_YES}"
	_log_msg "debug" "MSM_OPTION_DIR=${MSM_OPTION_DIR}, MSM_OPTION_DIR_PATH=${MSM_OPTION_DIR_PATH}, MSM_OPTION_COMPRESS=${MSM_OPTION_COMPRESS}"

	backup_check_option_input_directory

	case "${sub_command}" in
	create)
		backup_create "${create_time}"
		;;
	restore)
		backup_restore
		;;
	delete)
		backup_delete
		;;
	info)
		_check_info_file "backup" && _show_info "backup" || exit ${?}
		;;
	*)
		usage
		;;
	esac
}

main "${@}"

exit 0
