#!/bin/bash
#
# License: GPL 
# Original author: Blake, Kuo-Lien Huang
# Description:
#  2003/04/11 the first version, only work on /dev/hda1 & PXE
#  2003/06/01 disk to disk mode & etherboot support 
#             (the etherboot lzdsk images are located in
#              /tftpboot/etherboot-5.0.7 after DRBLCD installed)
#  2003/07/18 `checkInodeForDevice`
#
# Author: Steven Shiau <steven _at_ clonezilla org>
# 2003/07/21 the first version for Redhat, modified from Blake's version,
# only work on /dev/hda1 & PXE
# 2004/02/04 add the multicast function
# 2005/01/21 add code to save/restore some partitions (not entire disk).
# 2005/11/06 Many thanks to Christian Treczoks for providing "dd skip=1" method
# 2006/05/06 Remove save & restore hda1, since we can use save & restore partitions.
# 2006/12/07 Rewrite drbl-ocs to drbl-ocs + ocs-sr + ocs-functions.

# input parameters:
# save/savedisk/saveparts/restore/restoredisk/restoreparts/multicast_restore/multicast_restoredisk IMAGE-NAME DEVICES

# Notes:
# task_restorexxx/savexxx function will always use $ocsroot (/home/partimag) as mount point to do the save/restore, so it might overwrite the /home/partimag by mounting $ocsroot from server:/home2 (for example).
# The mount action is done in function task_preprocessing

ocs_file="$0"
ocs=`basename $ocs_file`
ocs_myself_id="$$"
# we need ocs_ppid for ocs-functions
ocs_ppid="$PPID"
# log file for sfdisk when restoring
# RESTORE_SFDISK_LOG is loaded from drbl.conf

# Some initial setting
confirm_before_clone="no"
ocs_sr_mode=""
gen_md5sum="no"
gen_sha1sum="no"
check_md5sum="no"
check_sha1sum="no"
gen_b2sum="no"
check_b2sum="no"
gen_chksum_for_files_in_dev="no"
chk_chksum_for_files_in_dev="no"
chk_img_restoreable_mod_save_def="yes"
chk_img_restoreable_mod_restore_def="yes"
# Flag to save restoring error log
save_restore_error_log="no"
# Flag to check target disk size before creating partition table
chk_tgt_disk_size_bf_mk_pt="yes"
# Flag to update EFI NVRAM after restoring a disk
update_efi_nvram=""
#
nfs_restart="no"
always_restore="no"
pxe_menu_default_mode=""
mcast_loop="1"
USE_NTFSCLONE="no"
nogui="off"   # Default to turn on TUI
FORCE_TO_USE_DD="no"
only_access_by_owner="yes"
force_dma_on="yes"
create_part="yes"
restore_mbr="yes"
restore_ebr="yes"
restore_prebuild_mbr="no"
rm_win_swap_hib="no"
rescue_mode="off"
select_img_in_client="no"
# If create_part_by_sfdisk=no, then we will use "dd if=$tgt_dir/mbr of=$sfdisk_target_hd skip=446 seek=446 bs=1 count=66" to dump the partition table.
create_part_by_sfdisk="yes"
# we want to make sfdisk --force if sfdisk is used.
sfdisk_opt="--force"
# Normally we do not dump MBR again when everything is done. However, This is an insurance for some hard drive has different numbers of cylinder, head and sector between image was saved and restored.". If the option is "yes", we will use dd to dump the MBR (total 512 Bytes, i.e. 446 bytes (executable code area) + 64 bytes (table of primary partitions) + 2 bytes (MBR signature; # 0xAA55) = 512 bytes) after disk was restored. 
dump_mbr_in_the_end="no"
# The flag to save or restore the hidden data between MBR and 1st partition.
clone_hidden_data="no"
# default output for udpcast stderr is surpressed.
# NOTE! Do not redirect it to standard output (stdin), otherwise partimage/ntfsclone pipe will get wrong image!
udpcast_stderr="/dev/null"
# default not to run those scripts in $OCS_PRERUN_DIR $OCS_POSTRUN_DIR
run_prerun_dir="no"
run_postrun_dir="no"
# default to put the ntfsclone progress in local /tmp/ (tmpfs)
ntfsclone_progress="local_tmp"
# Default to do CRC checking of partclone when restoring
do_partclone_crc_check="yes"
# Default to remove the Linux udev MAC address records on the restored GNU/Linux
do_rm_hardware_record="yes"
# Default to run ocs-update-syslinux to update the syslinux files (ldlinux.sys, *.c32 and *.bin)
do_update_syslinux="yes"
# Default not to put yes or no for encrypting the image dir
encrypt_ocs_img=""
# Default to remove the ntfs volume dirty flag after it's restored.
rm_ntfs_vol_dirty_flag="yes"
# Default to save the file system in the partition or LV. If no, it means to save other info as a disk image. This is only for some special cases.
do_fs_save="yes" 
# Default to run ocs-update-initrd to update the initramfs of restored OS
do_update_initrd="yes"
# Default not to play sound when job is done.
ocs_play_sound="no"
# Default to set netboot as 1st priority if run by drbl client ->
# efi_netboot_1st_in_nvram is from drbl-ocs.conf.
# By default we will check the partition table is MBR or GPT. If not one of them, quit.
do_check_part_table="yes"
# By default we do not enable read/write direct IO for Partclone
enable_dio="no"
# Put Clonezilla related log files to Clonezilla live USB drive or not.
put_log_usb=""

# Load DRBL setting and functions
DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"

. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
. /etc/drbl/drbl-ocs.conf
. $DRBL_SCRIPT_PATH/sbin/ocs-functions

# Load the config in ocs-live.conf. This is specially for Clonezilla live. It will overwrite some settings of /etc/drbl/drbl-ocs.conf, such as $DIA...
[ -e "/etc/ocs/ocs-live.conf" ] && . /etc/ocs/ocs-live.conf

force_TERM_as_linux_if_necessary
#
check_if_root

#
USAGE() {
    echo "Usage:"
    echo "To save or restore an image"
    echo "$ocs [OPTION] {savedisk|saveparts|restoredisk|restoreparts} IMAGE DEVICE"
    echo "IMAGE is the image dir name in $ocsroot, not absolute path"
    echo "DEVICE name can be with or without /dev/, e.g., /dev/sda or sda."
    echo "When in saving mode, the DEVICE name can also be assigned by:"
    echo "For disk: PTUUID or SERIALNO (short format), e.g., PTUUID=03c8b280-47aa-4881-aca5-9b9c66fe28c7." 
    echo "For partition: UUID or PARTUUID, e.g., UUID=0b51ce79-7bc0-4111-8a40-839461a9b12f"
    echo "When in resotoring mode, the DEVICE name can also be assigned by (for disk only):"
    echo "SERIALNO (short format), e.g., SERIALNO=2YK8XSYD."
    echo "If there are spaces in SERIALNO, replace every space by \"_\""
    echo 
    echo " Options for saving:"
    USAGE_common_save
    echo " -i, --image-size SIZE    Set the split image file volume size SIZE (MB). When $ocs is run with -x, the default SIZE is set as $VOL_LIMIT_IN_INTERACTIVE, if without -x, we will not split it."
    echo " -sfs, --skip-fs-save     Do not save the file system for device, just save the rest of files. This is mainly for BT from device massive deployment. A tag file 00-pseudo-img-note.txt will be added in the image dir, too."
    echo " -pe, --passwd-ecryptfs PASSWD  Set the password to encrypt the image. Use with option \"-enc\" when saving, or restoring when the image is encrypted. //NOTE// This is not a safe way to use it because the password is shown in the command line or system processes."
    echo " -pfe, --passwd-file-ecryptfs FILE  Set the password to encrypt the image in the FILE. Use with option -enc, or restoring when the image is encrypted. The file contains the password to encrypt the image. Its format is like: passphrase_passwd=YOURPASSWORD."
    echo " -plu, --put-log-files-usb  Put Clonezilla live related log files to Clonezilla live USB drive."
    echo " -scpt, --skip-check-part-table  Skip checking the partition table of the source disk, i.e., just save it without checking it is MBR or GPT format."
    echo " -luks, --enable-luks MODE  Open LUKS device or not. MODE can be either yes or no."
    USAGE_reserved_word_for_save
    echo
    echo " Options for restoring:"
    USAGE_common_restore
    echo " --mcast-port   NO     Assign the udp port number for multicast restore. This is used by clonezilla server. Normally it's not necessary to manually assign this option."
    echo " -ssnf, --skip-set-netboot-first    Skip setting 1st boot device as network in uEFI NVRAM of the restored machine."
    USAGE_reserved_word_for_restore
    echo
    echo " General options:"
    USAGE_common_general
    dialog_like_prog_help_prompt
    echo " -x, --interactive     Interactive mode to save or restore."
    echo " -os, --ocs-server SRV Assign the OCS server as SRV."
    echo
    echo "Example:"
    echo "   Before saving or restoring an image, you have to make sure the device is NOT mounted!"
    echo "   To save all the data in local 1st harddrive 'sda' as image 'IMAGE1', use ntfsclone, and lzop compression:"
    echo "   $ocs --use-ntfsclone -z3 savedisk IMAGE1 sda"
    echo
    echo "   To save the data in 1st and 2nd partition in local first harddrive 'sda' as image 'IMAGE2', use ntfsclone:"
    echo "   $ocs" '--use-ntfsclone -z3 saveparts IMAGE2 "sda1 sda2"'
    echo
    echo "   To restore the image "IMAGE1" to local sda, and grub-install will be run after cloning:"
    echo "   $ocs -g auto restoredisk IMAGE1 sda"
    echo
    echo "   To restore the image for the 1st and 2nd partition from "IMAGE2" to local sda1 and sda2, and grub-install will be run after restoring:"
    echo "   $ocs" '-g auto restoreparts IMAGE2 "sda1 sda2"'
    echo
    echo "   To save disk(s)/partition(s) as an image or restore an image to disk(s)/partition(s) interactively, use:"
    echo "   $ocs -x"
}
#
parse_ocs_sr_cmd_options_with_dash() {
 # Parse command-line options
 # It's UGLY to use "shift; n_shift=$((n_shift+1))"
 # However, we want to reuse the parse_ocs_sr_cmd_options_with_dash, 
 # a problem here:
 # The parameters (-b -g auto --nogui --max-time-to-wait 300 --language 0 multicast_restoreparts sarge-base "hda1 hda3" 2232), when put it as $*, it will
 # become (-b -g auto --nogui --max-time-to-wait 300 --language 0 multicast_restoreparts sarge-base hda1 hda3 2232), you see, "hda1 hda3" -> hda1 hda3
 # Then everyting is in a mess... so now the best solution I can find is to
 # calculate the n_shift, then parse it to original shell, not in the function.

 n_shift=0
 while [ $# -gt 0 ]; do
  case "$1" in
    -l|--language)
            shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      specified_lang="$1"
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$specified_lang" ] && USAGE && exit 1
	    ;;
    -g|--grub-install)  
            install_grub="on"
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
	      grub_partition="$(strip_leading_dev $1)"
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$grub_partition" ] && USAGE && exit 1
            ;;
    -f|--from-part)
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
	      source_part="$(strip_leading_dev $1)"
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$source_part" ] && USAGE && exit 1
            ;;
    -a|--no-force-dma-on)  
	    force_dma_on="no"
            shift; n_shift=$((n_shift+1));;
    -k|--no-fdisk|--no-create-partition)  
	    create_part="no"
            shift; n_shift=$((n_shift+1));;
    -k0)    # Do nothing. Just an option to let us explain the action easier.
	    true
            shift; n_shift=$((n_shift+1));;
    -k1|--fdisk-proportion)  
	    create_part="yes"
	    create_part_type="proportion"
            shift; n_shift=$((n_shift+1));;
    -k2|--fdisk-manual)  
	    create_part="yes"
	    create_part_type="manual"
            shift; n_shift=$((n_shift+1));;
    -np|--net-pipe)
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
	      ONTHEFLY_NET_PIPE=$1
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$ONTHEFLY_NET_PIPE" ] && USAGE && exit 1
            ;;
    --net-filter)
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              net_filter=$1
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$net_filter" ] && USAGE && exit 1
            ;;
    -noabo|--not-only-access-by-owner)  
            only_access_by_owner="no"
            shift; n_shift=$((n_shift+1));;
    -t|--no-restore-mbr)  
	    restore_mbr="no"
            shift; n_shift=$((n_shift+1));;
    -t1|--restore-raw-mbr)  
            # The flag to restore syslinux mbr.bin to MS windows system.
	    restore_prebuild_mbr="yes"
            shift; n_shift=$((n_shift+1));;
    -t2|--no-restore-ebr)  
	    restore_ebr="no"
            shift; n_shift=$((n_shift+1));;
    -u|--select-img-in-client)
	    select_img_in_client="yes"
            shift; n_shift=$((n_shift+1));;
    -e|--load-geometry)
	    load_HD_CHS_from_img="yes"
            shift; n_shift=$((n_shift+1));;
    -e1|--change-geometry)  
            change_ntfs_boot_chs="on"
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
	      ntfs_boot_partition="$(format_dev_name_with_leading_dev $1)"
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$ntfs_boot_partition" ] && USAGE && exit 1
            ;;
    -e2|--load-geometry-from-edd)
	    use_HD_CHS_from_EDD_pref="yes"
            shift; n_shift=$((n_shift+1));;
    -y|-y0|--always-restore|--always-restore-default-local)
	    always_restore="yes"
            pxe_menu_default_mode="local"
            shift; n_shift=$((n_shift+1));;
    -y1|--always-restore-default-clone)
	    always_restore="yes"
            pxe_menu_default_mode="clone"
            shift; n_shift=$((n_shift+1));;
    -y2|--always-restore-default-drbl)
	    always_restore="yes"
            pxe_menu_default_mode="drbl"
            shift; n_shift=$((n_shift+1));;
    -c|--confirm)
            confirm_before_clone="yes"
	    shift; n_shift=$((n_shift+1));;
    -sc|-scs|--skip-check-restorable|--skip-check-restorable-s)
            # Flag to check if the image is restorable
	    chk_img_restoreable_mod_save="no"
	    shift; n_shift=$((n_shift+1));;
    -scr|--skip-check-restorable-r)
            # Flag to check if the image is restorable
	    chk_img_restoreable_mod_restore="no"
	    shift; n_shift=$((n_shift+1));;
    -srel|--save-restore-error-log)
            # Flag to save restoring log
	    save_restore_error_log="yes"
	    shift; n_shift=$((n_shift+1));;
    -scpt|--skip-check-part-table)
            # Flag to save partition table of the source disk
            do_check_part_table="no"
	    shift; n_shift=$((n_shift+1));;
    -sfs|--skip-fs-save)
            # Flag to save file system
            do_fs_save="no"
	    shift; n_shift=$((n_shift+1));;
    -ssnf|--skip-set-netboot-first)
            # Flag to set netboot in the 1st priority
            efi_netboot_1st_in_nvram="no"
	    shift; n_shift=$((n_shift+1));;
    -w|--wait-time)
            shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      TIME_to_wait="$1"
              shift; n_shift=$((n_shift+1))
            fi
	    ;;
    --debug=?*)
            debug_level=${1#--debug=}
	    shift; n_shift=$((n_shift+1));;
    -b|-batch|--batch)
            ocs_batch_mode="on"
	    shift; n_shift=$((n_shift+1));;
    -d|--debug-mode)
            debug_mode="on"
	    shift; n_shift=$((n_shift+1));;
    -d0|--dialog)  
	    DIA="dialog" 
	    shift;;
    -d1|--Xdialog)  
	    DIA="Xdialog" 
	    shift;;
    -d2|--whiptail)  
	    DIA="whiptail" 
	    shift;;
    -d3|--gdialog)  
	    DIA="gdialog" 
	    shift;;
    -d4|--kdialog)  
	    DIA="kdialog" 
	    shift;;
    -enc|--enc-ocs-img)
            encrypt_ocs_img="yes"
	    shift; n_shift=$((n_shift+1));;
    -senc|--skip-enc-ocs-img)
            encrypt_ocs_img="no"
	    shift; n_shift=$((n_shift+1));;
    -pe|--passwd-ecryptfs)
            shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              passwd_ecryptfs=$1
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$passwd_ecryptfs" ] && USAGE && exit 1
            ;;
    -plu|--put-log-files-usb)
            put_log_usb="yes"
	    shift; n_shift=$((n_shift+1));;
    -pfe|--passwd-file-ecryptfs)
            shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              passwd_file_ecryptfs=$1
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$passwd_file_ecryptfs" ] && USAGE && exit 1
            ;;
    -m|--module)
            shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              module_to_load=$1
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$module_to_load" ] && USAGE && exit 1
            ;;
    -r|--resize-partition)
            resize_partition="on"
	    shift; n_shift=$((n_shift+1));;
    -v|--verbose)
	    verbose="on"
	    shift; n_shift=$((n_shift+1));;
    -z0|--no-compress)
            IMG_CLONE_CMP="cat"
	    shift; n_shift=$((n_shift+1));;
    -z1|--gzip-compress)
            IMG_CLONE_CMP="gzip -c $extra_gzip_opt"
	    shift; n_shift=$((n_shift+1));;
    -z1p|--smp-gzip-compress)
            IMG_CLONE_CMP="pigz -c $extra_pigz_opt"
	    shift; n_shift=$((n_shift+1));;
    -z2|--bz2-compress)
            IMG_CLONE_CMP="bzip2 -c $extra_bzip2_opt"
	    shift; n_shift=$((n_shift+1));;
    -z2p|--smp-bzip2-compress)
            if [ "$parallel_bzip2_prog" = "pbzip2" ]; then
              IMG_CLONE_CMP="pbzip2 -c $extra_pbzip2_opt"
            elif [ "$parallel_bzip2_prog" = "lbzip2" ]; then
              IMG_CLONE_CMP="lbzip2 -c $extra_lbzip2_opt"
	    fi
	    shift; n_shift=$((n_shift+1));;
    -z3|--lzo-compress)
            # Now only for ntfsclone, partimage not yet.
            IMG_CLONE_CMP="lzop -c $extra_lzop_opt"
	    shift; n_shift=$((n_shift+1));;
    -z4|--lzma-compress)
            IMG_CLONE_CMP="lzma -c $extra_lzma_opt"
	    shift; n_shift=$((n_shift+1));;
    -z5|--xz-compress)
            IMG_CLONE_CMP="xz -c $extra_xz_opt"
	    shift; n_shift=$((n_shift+1));;
    -z5p|--smp-xz-compress)
            IMG_CLONE_CMP="xz -c -T 0 $extra_xz_opt"
	    shift; n_shift=$((n_shift+1));;
    -z6|--lzip-compress)
            IMG_CLONE_CMP="lzip -c $extra_lzip_opt"
	    shift; n_shift=$((n_shift+1));;
    -z6p|--smp-lzip-compress)
            IMG_CLONE_CMP="plzip -c $extra_plzip_opt"
	    shift; n_shift=$((n_shift+1));;
    -z7|--lrzip-compress)
            # The option for lrzip is different from that of gzip (not -c)
            # Although lrzip uses "-p value      Set processor count to override number of threads" to assign the CPU number, however, from the manual of lrzip:
            # -p value
            # Set  the  number  of  processor count to determine the number of
            # threads to run.  Normally lrzip will scale according to the num‐
            # ber  of  CPUs  it detects. Using this will override the value in
            # case you wish to use less CPUs to either decrease  the  load  on
            # your  machine,  or  to improve compression. Setting it to 1 will
            # maximise compression but will not attempt to use more  than  one
            # CPU.
            # Therefore there is no need to assign a parallel version of lrzip.
            IMG_CLONE_CMP="lrzip -q $extra_lrzip_opt -"
	    shift; n_shift=$((n_shift+1));;
    -z8|--lz4-compress)
            IMG_CLONE_CMP="lz4 -c $extra_lz4_opt"
	    shift; n_shift=$((n_shift+1));;
    -z8p|--lz4mt-compress)
            IMG_CLONE_CMP="lz4 -T0 -c $extra_lz4mt_opt"
	    shift; n_shift=$((n_shift+1));;
    -z9|--zstd-compress)
	    IMG_CLONE_CMP="zstd -c $(echo $extra_zstd_opt | sed -r "s/-T[[:digit:]]+/-T1/")"
	    shift; n_shift=$((n_shift+1));;
    -z9p|--zstdmt-compress)
            IMG_CLONE_CMP="zstd -c $extra_zstd_opt"
	    shift; n_shift=$((n_shift+1));;
    -nogui|--nogui)
            # -nogui is for backward compatable, better to use --nogui
            nogui="on"
	    shift; n_shift=$((n_shift+1));;
    -ntfs-ok|--ntfs-ok)
            # if ntfs integrity is assumed OK, do not check
            ntfs_integrity_check="no"
	    shift; n_shift=$((n_shift+1));;
    -rm-win-swap-hib|--rm-win-swap-hib)
            # Remove page and hibernation files in MS windows
	    rm_win_swap_hib="yes"
	    shift; n_shift=$((n_shift+1));;
    -rescue|--rescue)
            rescue_mode="on"
	    shift; n_shift=$((n_shift+1));;
    -sfsck|--skip-fsck-src-part)
            fsck_src_part_intr="no"
            fsck_src_part_auto="no"
	    shift; n_shift=$((n_shift+1));;
    -fsck-src-part|--fsck-src-part|-fsck)
            fsck_src_part_intr="yes"
	    shift; n_shift=$((n_shift+1));;
    -fsck-src-part-y|--fsck-src-part-y|-fsck-y)
            fsck_src_part_auto="yes"
	    shift; n_shift=$((n_shift+1));;
    -mp|--mount-point)
            shift; n_shift=$((n_shift+1)); 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              mount_point=$1
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$mount_point" ] && USAGE && exit 1
	    ;;
    -or|--ocsroot)
            # overwrite the ocsroot in drbl.conf
            shift; n_shift=$((n_shift+1)); 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              ocsroot="$1"
              # The image path for bittorrent slice files will be affected
              ocsroot_btzone="$ocsroot/btzone"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$ocsroot" ] && USAGE && exit 1
	    ;;
    -i|--image-size)
	    shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              VOL_LIMIT=$1
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$VOL_LIMIT" ] && USAGE && exit 1
	    ;;
    --max-time-to-wait)
	    # ocs-sr need to know the --max-time-to-wait so that when sleeping
	    # between partitions restoring clone won't timeout.
	    shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      mcast_max_wait_time="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$mcast_max_wait_time" ] && USAGE && exit 1
	    ;;
    -q|--use-ntfsclone)
            USE_NTFSCLONE="yes"
	    shift; n_shift=$((n_shift+1));;
    -q1|--force-to-use-dd)
            FORCE_TO_USE_DD="yes"
	    shift; n_shift=$((n_shift+1));;
    -q2|--use-partclone)
            USE_PARTCLONE="yes"
	    shift; n_shift=$((n_shift+1));;
    -j|--create-part-by-sfdisk)
            # We leave this option for backward compatability.
            create_part_by_sfdisk="yes"
	    shift; n_shift=$((n_shift+1));;
    -j0|--create-part-by-dd)
            create_part_by_sfdisk="no"
	    shift; n_shift=$((n_shift+1));;
    -j1|--dump-mbr-in-the-end)
            dump_mbr_in_the_end="yes"
	    shift; n_shift=$((n_shift+1));;
    -j2|--clone-hidden-data)
	    clone_hidden_data="yes"
	    shift; n_shift=$((n_shift+1));;
    -icrc|--icrc)
	    do_partclone_crc_check="no"
	    shift; n_shift=$((n_shift+1));;
    -irvd|--irvd)
            rm_ntfs_vol_dirty_flag="no"
	    shift; n_shift=$((n_shift+1));;
    -irhr|--irhr)
	    do_rm_hardware_record="no"
	    shift; n_shift=$((n_shift+1));;
    -ius|--ius)
	    do_update_syslinux="no"
	    shift; n_shift=$((n_shift+1));;
    -iui|--iui)
	    do_update_initrd="no"
	    shift; n_shift=$((n_shift+1));;
    -hn0)
	    shift; n_shift=$((n_shift+1))
            change_win_hostname="By_IP"
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      win_hostname_prefix="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$win_hostname_prefix" ] && USAGE && exit 1
	    ;;
    -hn1)
	    shift; n_shift=$((n_shift+1))
            change_win_hostname="By_MAC"
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      win_hostname_prefix="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$win_hostname_prefix" ] && USAGE && exit 1
	    ;;
    -o|-o1|--run-postrun-dir) 
            # -o is for backward compatability
	    run_postrun_dir="yes"
	    shift; n_shift=$((n_shift+1));;
    -o0|--run-prerun-dir) 
	    run_prerun_dir="yes"
	    shift; n_shift=$((n_shift+1));;
    -ns|--ntfs-progress-in-image-dir)
	    ntfsclone_progress="image_dir"
	    shift; n_shift=$((n_shift+1));;
    -gm|--gen-md5sum)
	    gen_md5sum="yes"
	    shift; n_shift=$((n_shift+1));;
    -gs|--gen-sha1sum)
	    gen_sha1sum="yes"
	    shift; n_shift=$((n_shift+1));;
    -gb|--gen-b2sum)
	    gen_b2sum="yes"
	    shift; n_shift=$((n_shift+1));;
    -cm|--check-md5sum)
	    check_md5sum="yes"
	    shift; n_shift=$((n_shift+1));;
    -cs|--check-sha1sum)
	    check_sha1sum="yes"
	    shift; n_shift=$((n_shift+1));;
    -cb|--check-b2sum)
	    check_b2sum="yes"
	    shift; n_shift=$((n_shift+1));;
    -gmf|--gen-chksum-for-files-in-dev)
	    gen_chksum_for_files_in_dev="yes"
	    shift; n_shift=$((n_shift+1));;
    -cmf|--chk-chksum-for-files-in-dev)
	    chk_chksum_for_files_in_dev="yes"
	    shift; n_shift=$((n_shift+1));;
    -x|--interactive) 
	    ocs_sr_mode="interactive"
	    shift; n_shift=$((n_shift+1));;
    --mcast-port)
            shift; n_shift=$((n_shift+1)); 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              ocs_sr_mcast_port="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$ocs_sr_mcast_port" ] && USAGE && exit 1
	    ;;
    --data-port)
            shift; n_shift=$((n_shift+1)); 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              NC_PORT="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$NC_PORT" ] && USAGE && exit 1
	    ;;
    -p|--postaction)  
            shift; n_shift=$((n_shift+1)); 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      postaction="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$postaction" ] && USAGE && exit 1
            ;;
    -ps|--play-sound)
	    ocs_play_sound="yes"
	    shift; n_shift=$((n_shift+1));;
    --restore-only)
	    ocs_x_mode="restore_only"
	    shift; n_shift=$((n_shift+1));;
    -s|--source-IP)
            shift; n_shift=$((n_shift+1)); 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      SOURCE_IP="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            [ -z "$SOURCE_IP" ] && USAGE && exit 1
            ;;
    --save-only)
	    ocs_x_mode="save_only"
	    shift; n_shift=$((n_shift+1));;
    -os|--ocs-server)
            shift; n_shift=$((n_shift+1)); 
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              ocs_server="$1"
              shift; n_shift=$((n_shift+1)); 
            fi
            [ -z "$ocs_server" ] && USAGE && exit 1
	    ;;
    -um|--user-mode)
            shift; n_shift=$((n_shift+1)); 
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              ocs_user_mode="$1"
              shift; n_shift=$((n_shift+1)); 
            fi
            [ -z "$ocs_user_mode" ] && USAGE && exit 1
	    ;;
    -bm|--beginner-mode) 
	    ocs_user_mode="beginner"
	    shift; n_shift=$((n_shift+1));;
    -em|--expert-mode) 
	    ocs_user_mode="expert"
	    shift; n_shift=$((n_shift+1));;
    -icds|--ignore-chk-dsk-size-pt)
	    chk_tgt_disk_size_bf_mk_pt="no"
	    shift; n_shift=$((n_shift+1));;
    -iefi|--ignore-update-efi-nvram)
	    update_efi_nvram="no"
	    shift; n_shift=$((n_shift+1));;
    -luks|--enable-luks)
            shift; n_shift=$((n_shift+1)); 
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              enable_luks_mode="$1"
              shift; n_shift=$((n_shift+1)); 
            fi
            [ -z "$enable_luks_mode" ] && USAGE && exit 1
            case "$enable_luks_mode" in
              y|Y|[yY][eE][sS]) enable_luks="yes";;
              n|N|[nN][oO])     enable_luks="no";;
              *)
                 [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
                 echo "Unknown option for -luks|--enable-luks"
                 [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
                 echo $msg_delimiter_star_line
		 USAGE && exit 1;;
            esac
	    ;;
    -edio|--enable-direct-io)
	    enable_dio="yes"
	    shift; n_shift=$((n_shift+1));;
    -*)     echo "${0}: ${1}: invalid option" >&2
            USAGE >& 2
            exit 2 ;;
    *)      break ;;
  esac
 done
 if [ -z "$USE_NTFSCLONE" -a -z "$FORCE_TO_USE_DD" -a -z "$USE_PARTCLONE" ]; then
   # In this case, Partimage is used. However, if Partimage is not installed, we have to switch to Partclone.
   if ! type partimage >/dev/null 2>&1; then
     [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
     echo "Partimage was not found in this system. Force to use Partclone."
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     USE_PARTCLONE="yes"
   fi 
 fi

 [ -z "$ONTHEFLY_NET_PIPE" ] && ONTHEFLY_NET_PIPE="$ONTHEFLY_NET_PIPE_DEFAULT"
} # end of parse_ocs_sr_cmd_options_with_dash
#
save_only_dia_des() {
  savedisk_msg_1="savedisk"
  savedisk_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_save_local_disk")"
  saveparts_msg_1="saveparts"
  saveparts_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_save_local_parts")"
}
#
restore_only_dia_des() {
  restoredisk_msg_1="restoredisk"
  restoredisk_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_restore_local_disk")"
  restoreparts_msg_1="restoreparts"
  restoreparts_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_restore_local_parts")"
}
recovery_clonezilla_live_dia_des() {
  recovery_clonezilla_live_msg_1="recovery-iso-zip"
  recovery_clonezilla_live_msg_2="$(rep_whspc_w_udrsc "$msg_recovery_clonezilla_live")"
}
chk_img_restorable_dia_des() {
  chk_img_restorable_msg_1="chk-img-restorable"
  chk_img_restorable_msg_2="$(rep_whspc_w_udrsc "$msg_check_if_image_restorable")"
}
convert_img_compression_dia_des() {
  convert_img_compression_msg_1="cvt-img-compression"
  convert_img_compression_msg_2="$(rep_whspc_w_udrsc "$msg_convert_img_compression")"
}
encrypt_img_dia_des() {
  encrypt_img_msg_1="encrypt-img"
  encrypt_img_msg_2="$(rep_whspc_w_udrsc "$msg_encrypt_existing_img")"
}
decrypt_img_dia_des() {
  decrypt_img_msg_1="decrypt-img"
  decrypt_img_msg_2="$(rep_whspc_w_udrsc "$msg_decrypt_existing_img")"
}
p2v_img_dia_des() {
  p2v_img_msg_1="p2v-img"
  p2v_img_msg_2="$(rep_whspc_w_udrsc "$msg_p2v_existing_img")"
}
one_image_to_multiple_disks_dia_des() {
  one_2_m_disks_msg_1="1-2-mdisks"
  one_2_m_disks_msg_2="$(rep_whspc_w_udrsc "$msg_restore_1_image_to_multiple_local_disks")"
}
# functions to process options.
wrap_up_opt() {
  [ -z "$VOL_LIMIT" ] && VOL_LIMIT=$VOL_LIMIT_DEFAULT
  
  # change to other mount point for extra harddisk
  # Note: functions get_existing_disk_image, get_existing_parts_image and get_existing_partitions_from_img will use $imagedir 
  if [ -n "$mount_point" ]; then
     echo "Option -mp|--mount-point is assigned."
     echo "Using the image root directory $mount_point instead of $ocsroot."
     imagedir="$mount_point" 
  else
     imagedir="$ocsroot"
  fi
  
  check_ocs_input_params
  
  # Set/Reset init values
  PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT_INIT --debug=$debug_level"
  PARTIMAGE_RESTORE_OPT="$PARTIMAGE_RESTORE_OPT_INIT --debug=$debug_level"
  PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT_INIT"
  PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT_INIT"
  if [ "$nogui" = "on" ]; then
    # Partimage default uses TUI, if noguid, we have to run it with -B gui=no
    PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -B gui=no"
    PARTIMAGE_RESTORE_OPT="$PARTIMAGE_RESTORE_OPT -B gui=no"
  fi
  if [ "$nogui" = "off" ]; then
    # TUI is on.
    # Use the TUI mode for partclone (i.e. run with -N, otherwise by default partclone runs only text output)
    # //NOTE// From Partclone 0.2.52, if -N is set for partclone in save mode, the ncurse interface is able to be shown in stderr.
    PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT -N"
    PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT_INIT -N"
  fi
  if [ "$rescue_mode" = "on" ]; then
    PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT --rescue --force"
    # This PARTCLONE_RESTORE_OPT is for device to device cloning use 
    # when ocs-onthefly call ocs-sr, not for image restoring.
    # Actually when used in image restoring, not harmful for partclone to run.
    PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT --rescue --force"
  fi
  if [ "$do_partclone_crc_check" = "no" ]; then
    PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT --ignore_crc"
  fi
  
  echo $msg_delimiter_star_line
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  [ "$create_part" = "no" ] && echo "$msg_do_not_create_part"
  [ "$restore_mbr" = "no" ] && echo "$msg_do_not_restore_mbr"
  [ "$select_img_in_client" = "yes" ] && echo "$msg_you_have_to_input_image_names_in_client"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  
  # Append postaction, since we did not ask that in set_ocs_sr_extra_param and we might already set that.
  if [ -n "$postaction" -a \
       -z "$(echo $OCS_OPTS | grep -Ewo -- "-p")" ]
  then
       OCS_OPTS="$OCS_OPTS -p $postaction"
  fi
  if [ "$verbose" = "on" ]; then
    PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT -d"
    PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT -d"
    echo "OCS_OPTS: $OCS_OPTS"
    echo "PARTIMAGE_SAVE_OPT: $PARTIMAGE_SAVE_OPT"
    echo "PARTIMAGE_RESTORE_OPT: $PARTIMAGE_RESTORE_OPT"
    echo "PARTCLONE_SAVE_OPT: $PARTCLONE_SAVE_OPT"
    echo "PARTCLONE_RESTORE_OPT: $PARTCLONE_RESTORE_OPT"
    # The default output for udpcast stderr is surpressed, now turn it on
    udpcast_stderr="/dev/stderr"
  fi
  # If VOL_LIMIT is too large, split won't work. Check and fix it if required.
  check_and_fix_vol_limit_if_required
} # end of wrap_up_opt
#
wrap_up_opt_after_interactive_selection() {
  wrap_up_opt
  # For case when interactive mode the option "-scr" or "-scs" is used.
  # i.e. command like: "ocs-sr -x -scr" or "ocs-sr -x "-scs"

  case "$ocs_sr_type" in
    "savedisk"|"saveparts")
      if [ -z "$chk_img_restoreable_mod_save" ]; then
        chk_img_restoreable_mod_save="$chk_img_restoreable_mod_save_def"
      fi
      ;;
    "restoredisk"|"restoreparts")
      if [ -z "$chk_img_restoreable_mod_restore" ]; then
        chk_img_restoreable_mod_restore="$chk_img_restoreable_mod_restore_def"
      fi
      ;;
  esac
  # Shall we set default postaction if empty here?
  #[ -z "$postaction" ] && postaction="$POSTACTION_DEF"

} # end of wrap_up_opt_after_interactive_selection
#
run_ocs_sr_again_prompt() {
  local img dev
  # OCS_OPTS is a global variable
  img="$1"
  dev="$2"
  if [ "$ocs_sr_mode" = "interactive" ]; then
    run_again_fname="/tmp/ocs-$img-`date +%F-%H-%M`"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
    echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
    echo $0 $OCS_OPTS $ocs_sr_type $img $dev >> ${OCS_LOGFILE}
    echo $0 $OCS_OPTS $ocs_sr_type $img $dev | tee $run_again_fname
    echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
    echo $msg_delimiter_star_line | tee --append ${OCS_LOGFILE}
    if [ "$ocs_batch_mode" != "on" ]; then
      echo -n "$msg_press_enter_to_continue "
      read
    fi
  fi
} # end of run_ocs_sr_again_prompt
#
create_clonezilla_live_recovery_iso_zip() {
    # Target command Ex: 
    # ocs-iso -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
    # ocs-live-dev -c -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
    local OCS_PARAM_TMP ans_
    OCS_PARAM_TMP=`mktemp /tmp/ocs_recovery_tmp.XXXXXX`
    trap "[ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP" HUP INT QUIT TERM EXIT

    # Ask image name first (disk or parts image)
    if [ "$ocs_sr_img_name" = "ask_user" ]; then
      # Since get_target_dir_name_when_restoring_parts can search disk and parts image, we use this function instead of get_target_dir_name_when_restoring_disk
      get_target_dir_name_when_restoring_parts # get $target_dir
    else
      target_dir="$ocs_sr_img_name"
    fi
    check_input_target_image "$ocsroot/$target_dir"
    # Check if the image is disk or parts
    if [ -e "$ocsroot/$target_dir/disk" ]; then
      rec_dev_action=restoredisk
    else
      rec_dev_action=restoreparts
    fi

    # ask target disk/parts
    case "$rec_dev_action" in
     "restoredisk")
           dev_prompt_and_example="$msg_input_device_name_for_recovery_iso_zip ($msg_ex: 'hda' $msg_or 'sda' $msg_or 'hda hdb' $msg_or 'sda sdb') \n$msg_linux_disk_MS_mapping\n$msg_prompt_to_use_ask_user_for_later_choose"
	   # savedisk_preset is borrowed from drbl-ocs.conf
	   dev_preset="$(get_disk_list_from_img $ocsroot/$target_dir)"
           ;;
     "restoreparts")
           dev_prompt_and_example="$msg_input_device_name_for_recovery_iso_zip ($msg_ex: 'hda1 hda2' $msg_or 'sda1 sda2') \n$msg_linux_parts_MS_mapping\n$msg_prompt_to_use_ask_user_for_later_choose"
	   # saveparts_preset is borrowed from drbl-ocs.conf
	   dev_preset="$(get_parts_list_from_img $ocsroot/$target_dir)"
           ;;
    esac
    ASK_DEV_NAME=1
    while [ "$ASK_DEV_NAME" -ne 0 ]; do
      $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
      --inputbox "$dev_prompt_and_example" 0 0 "$dev_preset" 2> $OCS_PARAM_TMP
      rec_dev_name="$(cat $OCS_PARAM_TMP)"
      if [ -z "$rec_dev_name" ]; then
        $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --yesno "$msg_you_must_input_device_name_to_be_restored! $msg_do_u_want_to_do_it_again" 0 0 
         ans_="$?"
         case "$ans_" in
           0) # yes is chosen
              ASK_DEV_NAME=1;;
           1) # no is chosen
              echo "$msg_program_stop!"
              [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
              exit 1;;
         esac
      else
        ASK_DEV_NAME=0
      fi
    done
    echo > $OCS_PARAM_TMP # clean OCS_PARAM_TMP

    # ask if want to set ocs extra param for recovery iso/zip
    set_ocs_sr_extra_param restore $ocs_sr_type
    OCS_OPTS="$(cat $OCS_PARAM_TMP)"
    parse_ocs_sr_cmd_options_with_dash $OCS_OPTS
    OCS_OPTS="$(echo $OCS_OPTS)"  # make it in a line
    echo > $OCS_PARAM_TMP # clean OCS_PARAM_TMP

    # Ask language to be used in live iso/zip
    # $DRBL_SCRIPT_PATH/lang/bash/$lang
    get_existing_language $OCS_PARAM_TMP
    rec_lang="$(cat $OCS_PARAM_TMP)"

    # Ask keymap
    ASK_KEYMAP_FILE=1
    while [ "$ASK_KEYMAP_FILE" -ne 0 ]; do
      $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
      --inputbox "$keymap_file_prompt_and_example" 0 0 "NONE" 2> $OCS_PARAM_TMP
      rec_keymap_file="$(cat $OCS_PARAM_TMP)"
      if [ -z "$rec_keymap_file" ]; then
        $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
        --yesno "$msg_you_must_input_keymap_file! $msg_do_u_want_to_do_it_again" 0 0 
         ans_="$?"
         case "$ans_" in
           0) # yes is chosen
              ASK_KEYMAP_FILE=1;;
           1) # no is chosen
              echo "$msg_program_stop!"
              [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
              exit 1;;
         esac
      else
        ASK_KEYMAP_FILE=0
      fi
    done
    echo > $OCS_PARAM_TMP # clean OCS_PARAM_TMP

    # Generage iso, zip or both ?
    $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
    --menu "$msg_recovery_clonezilla_file_type $msg_choose_file_type" \
    0 0 0 \
    "iso"   "$msg_create_recovery_clonezilla_live_iso" \
    "zip"   "$msg_create_recovery_clonezilla_live_zip" \
    "both"  "$msg_create_recovery_clonezilla_live_iso_and_zip" \
    2> $OCS_PARAM_TMP
    rec_file_type="$(cat $OCS_PARAM_TMP)"

    [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP

    # Run ocs-iso/ocs-live-dev
    # Ex:ocs-iso -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
    #    ocs-live-dev -c -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
    (cd $ocsroot
    case "$rec_file_type" in
       iso)
          run_again_fname="/tmp/ocs-iso-zip-`date +%F-%H-%M`"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
          echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
          echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
          echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee $run_again_fname
	  [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
          echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
	  echo "$msg_the_output_file_is_in_dir: $ocsroot"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          echo -n "$msg_press_enter_to_continue"
          read 
          ocs-iso -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir 
          ;;
       zip)
          run_again_fname="/tmp/ocs-iso-zip-`date +%F-%H-%M`"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
          echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
          echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE} 
          echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee $run_again_fname
	  [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
          echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
	  echo "$msg_the_output_file_is_in_dir: $ocsroot"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          echo -n "$msg_press_enter_to_continue"
          read 
          ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir 
          ;;
       both)
          run_again_fname="/tmp/ocs-iso-zip-`date +%F-%H-%M`"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
          echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
          echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
          echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee $run_again_fname
          echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
          echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee -a $run_again_fname
	  [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
          echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
	  echo "$msg_the_output_file_is_in_dir: $ocsroot"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          echo -n "$msg_press_enter_to_continue"
          read 
          ocs-iso -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir 
          ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir 
          ;;
    esac
    )
} # end of create_clonezilla_live_recovery_iso_zip
#
get_extra_param_when_type_is_ask_user_and_interactive_mode() {
  if [ "$ocs_sr_type_assign" = "ask_user" -o \
       "$ocs_sr_mode" = "interactive" ]; then
   # since this ocs-sr asks for ocs_sr type, this means the option maybe not parsed.
   case "$ocs_sr_type" in
    save*)
     # ask if want to set ocs extra param
     OCS_PARAM_TMP=`mktemp /tmp/ocs_sr_param_tmp.XXXXXX`
     trap "[ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP" HUP INT QUIT TERM EXIT
     # Check LUKS device. If exists, ask if open it.
     check_luks_dev_then_ask_if_open_it # OCS_PARAM_TMP is the global variable

     set_ocs_sr_extra_param save $ocs_sr_type
     # In interactive mode, we reset OCS_OPTS instead of appending it. Later wrap_up_opt will process more.
     OCS_OPTS="$(cat $OCS_PARAM_TMP)"
     [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
     ;;
    restore*)
     # Skip the multicast_restore*, since it's spawned by drbl-ocs, not from ocs-sr
     # ask if want to set ocs extra param
     OCS_PARAM_TMP=`mktemp /tmp/ocs_sr_param_tmp.XXXXXX`
     trap "[ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP" HUP INT QUIT TERM EXIT
     set_ocs_sr_extra_param restore $ocs_sr_type
     # In interactive mode, we reset OCS_OPTS instead of appending it. Later wrap_up_opt will process more.
     OCS_OPTS="$(cat $OCS_PARAM_TMP)"
     [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
     ;;
    exit)
     echo "To use it again, run \"$ocs -x\"."
     exit 2
     ;;
   esac
   # Some options might be assigned in in /etc/ocs/ocs-live.conf. Here we have to add that.
   if [ -n "$ocs_sr_save_extra_opt" -o -n "$ocs_sr_restore_extra_opt" ]; then
     case "$ocs_sr_type" in
      *save*)    OCS_OPTS="$OCS_OPTS $ocs_sr_save_extra_opt" ;;
      *restore*) OCS_OPTS="$OCS_OPTS $ocs_sr_restore_extra_opt" ;;
     esac
   fi
   parse_ocs_sr_cmd_options_with_dash $OCS_OPTS
  fi
} # end of get_extra_param_when_type_is_ask_user_and_interactive_mode
#
do_savedisk_job(){
  task_preprocessing "$ocs_sr_type"

  # Set VOL_LIMIT_IN_INTERACTIVE 4096 if the image repo is using FAT file system.
  check_if_repo_fat_tune_image_vol_limit

  MULTIPATH_INFODIR="$(mktemp -d /tmp/multipath_info.XXXXXX)"

  parse_target_dir_with_reserved_name_saving_mode # obtain target_dir

  # Since /dev/md* is difficult to decide as disk or partition, just list both types, and disable devices list cache mechanism.
  if grep -Eq '[[:space:]]+md[0-9]+' /proc/partitions; then
    list_local_dev_mode="both_type_dev"
    use_dev_list_cache="no"
  fi

  if [ "$ocs_sr_dev" = "ask_user" ]; then
    # To get $target_hd
    get_target_hd_name_from_local_machine "$msg_local_source_disk \n$msg_linux_disk_naming $msg_press_space_to_mark_selection"
    target_hd="$(select_VG "$target_hd")"
  elif [ "$ocs_sr_dev" = "all" -o \
         "$ocs_sr_dev" = "unmounted_disk" ]; then
    get_not_busy_disks_or_parts harddisk "" ""  # we will get dev_list
    target_hd="$dev_list"
  elif [ -n "$(echo "$ocs_sr_dev" | grep -Ei "(PTUUID=|SERIALNO=)")" ]; then
    # Input might be multiple devices like: 
    # PTUUID="f722833a" PTUUID="271e1971" SERIALNO="36000c292124c4d6554f924089bd9b83a"
    # PTUUID="f722833a" sdb

    # Format the input PTUUID= and SERIALNO= to be uppercase
    ocs_sr_dev="$(echo $ocs_sr_dev | sed -r -e "s/ptuuid=/PTUUID=/gi" -e "s/serialno=/SERIALNO=/gi")"
    target_hd=""
    for id in $ocs_sr_dev; do
      found_hd=""
      if [ -n "$(echo "$id" | grep -Ei "^PTUUID=")" ]; then
        # E.g., PTUUID="f722833a"
        get_block_dev_from_PTUUID $id    # we will get blk_dev
        found_hd="$blk_dev"
        if [ -n "$found_hd" ]; then
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Found $id is device: $found_hd"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          if [ -z "$(echo $target_hd | grep -Ewo "$found_hd")" ]; then
            # Avoid duplication
            target_hd="$target_hd $found_hd"
          else
            [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
            echo "Assigned device $found_hd is already in the list."
            [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          fi
        fi
      elif [ -n "$(echo "$id" | grep -Ei "^SERIALNO=")" ]; then
        # E.g., SERIALNO="36000c292124c4d6554f924089bd9b83a"
        get_block_dev_from_SERIALNO $id   # we will get blk_dev
        found_hd="$blk_dev"
        if [ -n "$found_hd" ]; then
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Found $id is device: $found_hd"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          if [ -z "$(echo $target_hd | grep -Ewo "$found_hd")" ]; then
            # Avoid duplication
            target_hd="$target_hd $found_hd"
          else
            [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
            echo "Assigned device $found_hd is already in the list."
            [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          fi
        fi
      else
        # E.g., sdb
        if [ -z "$(echo $target_hd | grep -Ewo "$id")" ]; then
          # Avoid duplication
          target_hd="$target_hd $id"
        else
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Assigned device $id is already in the list."
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        fi
      fi
    done
  else
    target_hd="$ocs_sr_dev"
  fi
  # check if the device exists
  ANS_TMP=`mktemp /tmp/ocs_chkdev.XXXXXX`
  trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
  check_if_input_device_exist $ANS_TMP $target_hd 
  target_hd="$(cat $ANS_TMP | tr -d \")"
  [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
  get_extra_param_when_type_is_ask_user_and_interactive_mode
  wrap_up_opt_after_interactive_selection  # Since extra param inputed again, wrap up again

  run_ocs_sr_again_prompt "$target_dir" "$target_hd"

  # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
  # The original ones will be kept as ocsroot_orig and target_dir_orig.
  prepare_ecryptfs_mount_point_if_necessary

  task_processing_after_parameters_checked

  ocs-run-boot-param ocs_savedisk_prerun
  rc_savedisk=""
  task_savedisk "$target_dir" "$target_hd"
  rc_savedisk="$?"
  ocs-run-boot-param ocs_savedisk_postrun

  #
  if [ -d "$MULTIPATH_INFODIR" -a \
       -n "$(echo $MULTIPATH_INFODIR | grep -w "multipath_info")" ]; then
    rm -rf "$MULTIPATH_INFODIR"
  fi

  # Dump software RAID info
  dump_software_raid_info_if_exists $ocsroot/$target_dir

  # Dump hardware info
  dump_hardware_software_info $ocsroot/$target_dir

  if [ "$ocs_sr_mode" = "interactive" ]; then
    echo $0 $OCS_OPTS $ocs_sr_type "$target_dir" "$target_hd" > $ocsroot/$target_dir/Info-saved-by-cmd.txt
  else
    echo $0 $orig_ocs_opt > $ocsroot/$target_dir/Info-saved-by-cmd.txt
  fi

  task_postprocessing "$ocs_sr_type" "$target_dir"
} # end of do_savedisk_job
#
do_restoredisk_job(){
  task_preprocessing "$ocs_sr_type"
  MULTIPATH_INFODIR="$(mktemp -d /tmp/multipath_info.XXXXXX)"

  if [ "$ocs_sr_img_name" = "ask_user" ]; then
    get_target_dir_name_when_restoring_disk  # get $target_dir
  elif [ "$ocs_sr_img_name" = "autoproductname" ]; then
    # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
    target_dir="$(get_vendor_product_name)-img"
  else
    target_dir="$ocs_sr_img_name"
  fi

  check_input_target_image "$ocsroot/$target_dir"
  source_hd_no="$(get_disk_list_from_img $ocsroot/$target_dir | sed -e "s/ *$//g" | wc -w | awk '{print $1}')"

  # Since /dev/md* is difficult to decide as disk or partition, just list both types, and disable devices list cache mechanism.
  if grep -Eq '^md[0-9]+' $ocsroot/$target_dir/blkdev.list; then
    list_local_dev_mode="both_type_dev"
    use_dev_list_cache="no"
  fi

  if [ "$ocs_sr_dev" = "ask_user" ]; then
    if [ "$source_hd_no" -eq 1 ]; then
      dia_sel_opt="menu"
    else
      dia_sel_opt="checklist"
    fi
    # To get $target_hd
    get_target_hd_name_from_local_machine "$msg_choose_the_disks_to_restore \n$msg_linux_disk_naming $msg_press_space_to_mark_selection" $dia_sel_opt
  elif [ "$ocs_sr_dev" = "all" -o \
         "$ocs_sr_dev" = "unmounted_disk" ]; then
    get_not_busy_disks_or_parts harddisk "" ""  # we will get dev_list
    target_hd="$dev_list"
    target_hd_no="$(LC_ALL=C echo $target_hd | wc -w)"
    if [ "$source_hd_no" != "$target_hd_no" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "The source disk number from the image does not match to that of destination disk." | tee --append ${OCS_LOGFILE}
      echo "Source disk number: $source_hd_no" | tee --append ${OCS_LOGFILE}
      echo "Destination disk number: $target_hd_no" | tee --append ${OCS_LOGFILE}
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      echo -n "$msg_press_enter_to_continue..."
      read
      echo "$msg_program_stop." | tee --append ${OCS_LOGFILE}
      exit 1
    fi
  elif [ "$ocs_sr_dev" = "auto-matchsize" ]; then
    get_not_busy_disks_or_parts harddisk "" ""  # we will get dev_list
    target_hd="$dev_list"
    target_hd_no="$(LC_ALL=C echo $target_hd | wc -w)"
    if [ "$source_hd_no" != "$target_hd_no" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "The source disk number from the image does not match to that of destination disk." | tee --append ${OCS_LOGFILE}
      echo "Source disk number: $source_hd_no" | tee --append ${OCS_LOGFILE}
      echo "Destination disk number: $target_hd_no" | tee --append ${OCS_LOGFILE}
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      echo -n "$msg_press_enter_to_continue..."
      read
      echo "$msg_program_stop." | tee --append ${OCS_LOGFILE}
      exit 1
    fi
    # Since it's auto-matchsize mode, we have to reorder the destination disks.
    # (1) Find the sizes of source disks
    # (2) Map them
    # TODO:
    source_hd_no="$(get_disk_list_from_img $ocsroot/$target_dir | sed -e "s/ *$//g")"
  elif [ -n "$(echo "$ocs_sr_dev" | grep -Ei "SERIALNO=")" ]; then
    # Input might be multiple devices like: 
    # SERIALNO="36000c292124c4d6554f924089bd9b83a" sdb

    # Format the input SERIALNO= to be uppercase
    ocs_sr_dev="$(echo $ocs_sr_dev | sed -r -e "s/serialno=/SERIALNO=/gi")"
    target_hd=""
    for id in $ocs_sr_dev; do
      found_hd=""
      if [ -n "$(echo "$id" | grep -Ei "^SERIALNO=")" ]; then
        # E.g., SERIALNO="36000c292124c4d6554f924089bd9b83a"
        get_block_dev_from_SERIALNO $id   # we will get blk_dev
        found_hd="$blk_dev"
        if [ -n "$found_hd" ]; then
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Found $id is device: $found_hd"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          if [ -z "$(echo $target_hd | grep -Ewo "$found_hd")" ]; then
            # Avoid duplication
            target_hd="$target_hd $found_hd"
          else
            [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
            echo "Assigned device $found_hd is already in the list."
            [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          fi
        fi
      else
        # E.g., sdb
        if [ -z "$(echo $target_hd | grep -Ewo "$id")" ]; then
          # Avoid duplication
          target_hd="$target_hd $id"
        else
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Assigned device $id is already in the list."
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        fi
      fi
    done
  else
    target_hd="$ocs_sr_dev"
  fi
  get_extra_param_when_type_is_ask_user_and_interactive_mode
  wrap_up_opt_after_interactive_selection  # Since extra param inputed again, wrap up again

  run_ocs_sr_again_prompt "$target_dir" "$target_hd"

  # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
  # The original ones will be kept as ocsroot_orig and target_dir_orig.
  prepare_ecryptfs_mount_point_if_necessary

  task_processing_after_parameters_checked

  ocs-run-boot-param ocs_restoredisk_prerun
  task_restoredisk "$target_dir" "$target_hd"
  ocs-run-boot-param ocs_restoredisk_postrun

  #
  if [ -d "$MULTIPATH_INFODIR" -a \
       -n "$(echo $MULTIPATH_INFODIR | grep -w "multipath_info")" ]; then
    rm -rf "$MULTIPATH_INFODIR"
  fi

  task_postprocessing "$ocs_sr_type" "$target_dir"
} # end of do_restoredisk_job
#
do_saveparts_job() {
  task_preprocessing "$ocs_sr_type"

  # Set VOL_LIMIT_IN_INTERACTIVE 4096 if the image repo is using FAT file system.
  check_if_repo_fat_tune_image_vol_limit

  parse_target_dir_with_reserved_name_saving_mode # obtain target_dir

  # Since /dev/md* is difficult to decide as disk or partition, just list both types, and disable devices list cache mechanism.
  if grep -Eq '[[:space:]]+md[0-9]+' /proc/partitions; then
    list_local_dev_mode="both_type_dev"
    use_dev_list_cache="no"
  fi

  if [ "$ocs_user_mode" = "expert" ] && [ -z "$list_local_dev_mode" ]; then
    ask_list_device_mode  # get $list_local_dev_mode
  fi
  if [ "$ocs_sr_dev" = "ask_user" ]; then
    if [ "$list_local_dev_mode" = "both_type_dev" ]; then
      get_target_device_name_from_local_machine # get $tgt_devs
      target_parts="$tgt_devs"
    else
      get_target_parts_name_from_local_machine  # get $target_parts
    fi
  elif [ "$ocs_sr_dev" = "all" -o \
         "$ocs_sr_dev" = "unmounted_parts" ]; then
    get_not_busy_disks_or_parts partition "" ""  # we will get dev_list
    target_parts="$dev_list"
  elif [ -n "$(echo "$ocs_sr_dev" | grep -Ei "(UUID=|PARTUUID=)")" ]; then
    # Input might be multiple devices like: 
    # UUID="B5F8-50F8" UUID="46f141f2-8ea6-47e4-81f0-052cb95a5e79" PARTUUID="271e1971-01"
    # UUID="B5F8-50F8" sda5 PARTUUID="271e1971-01"

    # Format the input PARTUUID= (must be 1st in sed) and UUID=
    # to be uppercase
    ocs_sr_dev="$(echo $ocs_sr_dev | sed -r -e "s/partuuid=/PARTUUID=/gi" -e "s/uuid=/UUID=/gi")"
    target_parts=""
    for id in $ocs_sr_dev; do
      found_part=""
      if [ -n "$(echo "$id" | grep -Ei "^PARTUUID=")" ]; then
        # E.g., PARTUUID="f722833a-01"
        get_block_dev_from_PARTUUID $id   # we will get blk_dev
        found_part="$blk_dev"
        if [ -n "$found_part" ]; then
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Found $id is device: $found_part"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          if [ -z "$(echo $target_parts | grep -Ewo "$found_part")" ]; then
            # Avoid duplication
            target_parts="$target_parts $found_part"
          else
            [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
            echo "Assigned device $found_part is already in the list."
            [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          fi
        fi
      elif [ -n "$(echo "$id" | grep -Ei "^UUID=")" ]; then
        # E.g., UUID="f722833a"
        found_part="$(LC_ALL=C blkid --uuid ${id/UUID=/})"
        found_part=${found_part#/dev/*}
        if [ -n "$found_part" ]; then
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Found $id is device: $found_part"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          if [ -z "$(echo $target_parts | grep -Ewo "$found_part")" ]; then
            # Avoid duplication
            target_parts="$target_parts $found_part"
          else
            [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
            echo "Assigned device $found_part is already in the list."
            [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
          fi
        fi
      else
        # E.g., sdb
        if [ -z "$(echo $target_parts | grep -Ewo "$id")" ]; then
            # Avoid duplication
          target_parts="$target_parts $id"
        else
          [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
          echo "Assigned device $id is already in the list."
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        fi
      fi
    done
  else
    target_parts="$ocs_sr_dev"
  fi
  
  # check if the device exists
  ANS_TMP=`mktemp /tmp/ocs_chkdev.XXXXXX`
  trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
  check_if_input_device_exist $ANS_TMP $target_parts
  # we have to remove " (comes with checklist in dialog) so that for loop
  # will work (Specially for FC3/4...)
  target_parts="$(cat $ANS_TMP | tr -d \")"
  [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
  get_extra_param_when_type_is_ask_user_and_interactive_mode
  wrap_up_opt_after_interactive_selection  # Since extra param inputed again, wrap up again

  run_ocs_sr_again_prompt "$target_dir" "$target_parts"

  # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
  # The original ones will be kept as ocsroot_orig and target_dir_orig.
  prepare_ecryptfs_mount_point_if_necessary

  task_processing_after_parameters_checked

  ocs-run-boot-param ocs_saveparts_prerun
  rc_saveparts=""
  task_saveparts "$target_dir" "$target_parts"
  rc_saveparts="$?"
  ocs-run-boot-param ocs_saveparts_postrun

  #
  if [ -d "$MULTIPATH_INFODIR" -a \
       -n "$(echo $MULTIPATH_INFODIR | grep -w "multipath_info")" ]; then
    rm -rf "$MULTIPATH_INFODIR"
  fi

  # Dump software RAID info
  dump_software_raid_info_if_exists $ocsroot/$target_dir

  # Dump hardware info
  dump_hardware_software_info $ocsroot/$target_dir

  if [ "$ocs_sr_mode" = "interactive" ]; then
    echo $0 $OCS_OPTS $ocs_sr_type "$target_dir" "$target_parts" > $ocsroot/$target_dir/Info-saved-by-cmd.txt
  else
    echo $0 $orig_ocs_opt > $ocsroot/$target_dir/Info-saved-by-cmd.txt
  fi

  task_postprocessing "$ocs_sr_type" "$target_dir"
} # end of do_saveparts_job
#
do_restoreparts_job(){
  task_preprocessing "$ocs_sr_type"

  if [ "$ocs_sr_img_name" = "ask_user" ]; then
    get_target_dir_name_when_restoring_parts  # get $target_dir
  elif [ "$ocs_sr_img_name" = "autoproductname" ]; then
    # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
    target_dir="$(get_vendor_product_name)-img"
  else
    target_dir="$ocs_sr_img_name"
  fi
  check_input_target_image "$ocsroot/$target_dir"
  # This is actually the max avaiable partitions number from image
  source_parts_no="$(get_parts_list_from_img $ocsroot/$target_dir | sed -e "s/ *$//g" | wc -w | awk '{print $1}')"
  
  if [ "$ocs_sr_dev" = "ask_user" ]; then
    if [ -z "$source_part" ]; then
      ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
      trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
      get_existing_partitions_from_img $ANS_TMP $ocsroot/$target_dir no select_from_img
      # we have to remove " (comes with checklist in dialog) 
      # so that for loop will work (Specially for FC3/4...)
      source_part="$(cat $ANS_TMP | tr -d \")"
      [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
    fi

    # Since /dev/md* is difficult to decide as disk or partition, just list both types, and disable devices list cache mechanism.
    if grep -Eq '^md[0-9]+' $ocsroot/$target_dir/blkdev.list; then
      list_local_dev_mode="both_type_dev"
      use_dev_list_cache="no"
    fi

    # target name exists, but file "parts" is empty ?
    check_target_parts $ocsroot/$target_dir/parts "$source_part"
    # User might choose the partitions when it's ask_user, so here we have to calculate the number again.
    source_parts_no="$(LC_ALL=C echo $source_part | wc -w)"
    if [ "$source_parts_no" -eq 1 ]; then
      dia_sel_opt="menu"
      if [ "$ocs_user_mode" = "expert" ] && [ -z "$list_local_dev_mode" ]; then
        ask_list_device_mode  # get $list_local_dev_mode
      fi
      if [ "$list_local_dev_mode" = "both_type_dev" ]; then
        get_target_device_name_from_local_machine "$msg_choose_the_parts_to_restore \n$msg_linux_parts_MS_mapping" $dia_sel_opt  # Obtain tgt_devs
        target_parts="$tgt_devs"
      else
        get_target_parts_name_from_local_machine "$msg_choose_the_parts_to_restore \n$msg_linux_parts_MS_mapping" $dia_sel_opt  # Obtain target_parts
      fi
    else
      # In this case, it's too complicated to restore partitions to different partitions on destination disk.
      # Therefore we only allow original partitions to partitions, i.e. e.g., sda1, sda2 from image -> sda1, sda2 on destination disk. Therefore "source_part" is reset to none, i.e., not to convert the image to different device name's image.
      target_parts="$source_part"
      source_part=""
      if [ "$ocs_batch_mode" != "on" ]; then
        [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
        echo "$msg_2_or_more_parts_only_same_parts_on_dest"
        [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        echo -n "$msg_press_enter_to_continue"
        read 
      fi
    fi
  elif [ "$ocs_sr_dev" = "all" -o \
         "$ocs_sr_dev" = "unmounted_parts" ]; then
    get_not_busy_disks_or_parts partition "" ""  # we will get dev_list
    target_parts="$dev_list"
    target_parts_no="$(LC_ALL=C echo $target_parts | wc -w)"
    if [ "$source_parts_no" != "$target_parts_no" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "The source partition(s) number from the image does not match to that of destination partition(s)." | tee --append ${OCS_LOGFILE}
      echo "Source partition(s) number: $source_parts_no" | tee --append ${OCS_LOGFILE}
      echo "Destination partition(s) number: $target_parts_no" | tee --append ${OCS_LOGFILE}
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      echo -n "$msg_press_enter_to_continue..."
      read
      echo "$msg_program_stop." | tee --append ${OCS_LOGFILE}
      exit 1
    fi
  else
    target_parts="$ocs_sr_dev"
  fi
  #
  get_extra_param_when_type_is_ask_user_and_interactive_mode
  wrap_up_opt_after_interactive_selection  # Since extra param inputed again, wrap up again
  # Append the option if restoring to different partition on the destination disk.
  if [ -n "$source_part" ]; then
      OCS_OPTS="$OCS_OPTS -f $source_part"
  fi

  run_ocs_sr_again_prompt "$target_dir" "$target_parts"

  # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
  # The original ones will be kept as ocsroot_orig and target_dir_orig.
  prepare_ecryptfs_mount_point_if_necessary

  task_processing_after_parameters_checked

  ocs-run-boot-param ocs_restoreparts_prerun
  task_restoreparts "$target_dir" "$target_parts"
  ocs-run-boot-param ocs_restoreparts_postrun

  task_postprocessing "$ocs_sr_type" "$target_dir"
} # end of do_restoreparts_job

# OCS_OPT is for drbl-ocs using, will not put into the partimage command options
# PARTIMAGE_RESTORE_OPT & PARTIMAGE_SAVE_OPT will put into partimage command options
PARTIMAGE_SAVE_OPT_INIT=""
PARTIMAGE_RESTORE_OPT_INIT=""
OCS_OPT=""
report_msg=""

# The parameters, example: 
# (-b -g auto --nogui --max-time-to-wait 300 --language 0 multicast_restoreparts sarge-base "hda1 hda3" 2232) ->
# (multicast_restoreparts sarge-base "hda1 hda3" 2232)
orig_ocs_opt="$*"
parse_ocs_sr_cmd_options_with_dash $*
shift ${n_shift}
ocs_sr_type_assign="$1"
ocs_sr_img_name="$2"
shift 2
ocs_sr_dev="$(strip_leading_dev $*)"

# Rotate the log file
ocs_log_rotate $OCS_LOGFILE
ocs_log_rotate $PARTCLONE_LOG

#
echo "Starting $0 at "$(LC_ALL=C date +%F' '%T' '%Z)"..." | tee --append ${OCS_LOGFILE}

#
ask_and_load_lang_set $specified_lang

# check DIA
check_DIA_set_ESC $DIA

##############
#### MAIN ####
##############

# Create log dir
mkdir -p $ocs_log_dir

# ocs root
if [ ! -d "$ocsroot" ]; then
  echo "Please make sure your $ocsroot is a directory!!!"
  # The common error is user try to create a ocsroot as /home/partimage, the default is /home/partimag (without e)
  if [ "$ocsroot" = "/home/partimag" -a -d "/home/partimage" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$msg_home_partimag_not_home_partimage" 
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  fi
  echo "We can NOT go on! Press \"c\" to enter command prompt or any other key to quit the program..." 
  read fail_answer
  case "$fail_answer" in
    [cC]) sulogin ;;
       *) exit 1 ;;
  esac
fi

# Prepare some cache files about disk/partition list and file system, size...
ocs-prep-cache

#
if [ "$ocs_sr_type_assign" = "ask_user" -o \
     "$ocs_sr_mode" = "interactive" ]; then
  ASK_OCS_SR_TYPE=1
  case "$ocs_x_mode" in
    "save_only")
      save_only_dia_des
      ;;
    "restore_only")
      restore_only_dia_des
      ;;
    *) # Both when image exists, or only save menu
      save_only_dia_des
      if check_if_any_image_exists; then
        restore_only_dia_des
      fi
      ;;
  esac
  if [ "$ocs_sr_extra_restore_mode" != "no" ]; then
    # If there is any image in $ocsroot, we can create recovery clonezilla live iso/zip and check the image.
    if check_if_any_image_exists; then
      recovery_clonezilla_live_dia_des
      chk_img_restorable_dia_des
      one_image_to_multiple_disks_dia_des
      convert_img_compression_dia_des
      encrypt_img_dia_des
      decrypt_img_dia_des
      if type qemu-img &>/dev/null && type kvm &>/dev/null; then
        p2v_img_dia_des
      fi
    fi
  fi

  #
  [ -z "$ocs_user_mode" ] && ask_if_beginner_or_expert_mode

  # Clean the previous saved env so if ocs-onthefly is cancelled, won't read the previous env and reboot/poweroff.
  rm -f /var/lib/clonezilla/ocs-vars

  #
  echo "Choose the mode for ocs-sr"
  TMP="$(mktemp /tmp/menu-ocs.XXXXXX)"
  trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
  while [ "$ASK_OCS_SR_TYPE" -ne 0 ]; do
    $DIA --backtitle "$msg_nchc_free_software_labs" \
    --title "$msg_nchc_clonezilla: $msg_choose_mode" \
    --menu "$msg_clonezilla_is_free_and_no_warranty\n$msg_overwrite_data_on_disk_when_restoring\n$msg_hint_multiple_choice_select_by_space\n$msg_choose_mode_ocs_sr" \
    0 0 0 \
    $savedisk_msg_1 $savedisk_msg_2 \
    $saveparts_msg_1 $saveparts_msg_2 \
    $restoredisk_msg_1 $restoredisk_msg_2 \
    $restoreparts_msg_1 $restoreparts_msg_2 \
    $one_2_m_disks_msg_1 $one_2_m_disks_msg_2 \
    $recovery_clonezilla_live_msg_1 $recovery_clonezilla_live_msg_2 \
    $chk_img_restorable_msg_1 $chk_img_restorable_msg_2 \
    $convert_img_compression_msg_1 $convert_img_compression_msg_2 \
    $encrypt_img_msg_1 $encrypt_img_msg_2 \
    $decrypt_img_msg_1 $decrypt_img_msg_2 \
    $p2v_img_msg_1 $p2v_img_msg_2 \
    "exit"           "$msg_exit. $msg_enter_cml" \
    2> $TMP
    ocs_sr_type="$(cat $TMP)"
    if [ -z "$ocs_sr_type" ]; then
      ASK_OCS_SR_TYPE=1
    else
      ASK_OCS_SR_TYPE=0
    fi
  done
  [ -e "$TMP" ] && rm -f $TMP
  [ "$ocs_sr_type" = "exit" ] && exit 0
  [ -z "$ocs_sr_type" ] && echo "You must specify the action! Program terminated!!!" && exit 1
  # If ocs_sr_type from ask_user, then we should let ocs_sr_img_name & ocs_sr_dev as ask_user, too
  ocs_sr_img_name="ask_user"
  ocs_sr_dev="ask_user"
  # Overwrite the default HALT_REBOOT_OPT (-f) so that if user choose to halt/reboot, it will use normal halt/reboot sequence, so the mounted device will be unmounted.
  HALT_REBOOT_OPT="-f"
  VOL_LIMIT="$VOL_LIMIT_IN_INTERACTIVE"
else
  ocs_sr_type="$ocs_sr_type_assign"
fi

# ocs_mode_prompt is to be shown in the title of dialog menu later
ocs_mode_prompt="$ocs_sr_type"

echo $msg_delimiter_star_line | tee --append $OCS_LOGFILE
# Clonezilla image home
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
echo "Clonezilla image dir: $ocsroot" | tee --append $OCS_LOGFILE
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
[ ! -d "$ocsroot" ] && mkdir -p $ocsroot
  
# Some options might be assigned in in /etc/ocs/ocs-live.conf. Here we have to add that.
if [ -n "$ocs_sr_save_extra_opt" -o -n "$ocs_sr_restore_extra_opt" ]; then
  case "$ocs_sr_type" in
   *save*)    OCS_OPTS="$OCS_OPTS $ocs_sr_save_extra_opt" ;;
   *restore*) OCS_OPTS="$OCS_OPTS $ocs_sr_restore_extra_opt" ;;
  esac
  parse_ocs_sr_cmd_options_with_dash $OCS_OPTS
fi

# Decide the mode for update_efi_nvram if it's not assigned. The EFI NVRAM is only updated when the mode is restoredisk or multicast_restoredisk
if [ -z "$update_efi_nvram" ]; then
  case "$ocs_sr_type" in
   *restoredisk) update_efi_nvram="yes";;
   *) update_efi_nvram="no";;
  esac
fi

# Process options. //NOTE// There might be twice of wrap_up_opt, one is for the command options assigned, the other one is the command options got from interactive mode.
wrap_up_opt

# Before starting, we turn off swap and LVM2, unlock the busy partitions.
turn_off_swap_and_LVM2

# Boot parameter ocs_overwrite_postaction has higher priority than the action assigned by option "-p".
overwrite_postaction_if_assigned $ocs_sr_type

#
case "$ocs_sr_type" in
  "savedisk")            do_savedisk_job ;;
  "restoredisk")         do_restoredisk_job ;;
  "saveparts")           do_saveparts_job ;;
  "restoreparts")        do_restoreparts_job ;;
  "recovery-iso-zip")    create_clonezilla_live_recovery_iso_zip ;;
  "chk-img-restorable")  ocs-chkimg ;;
  "cvt-img-compression") ocs-cvtimg-comp ;;
  "encrypt-img")         ocs-encrypt-img ;;
  "decrypt-img")         ocs-decrypt-img ;;
  "p2v-img")             ocs-img-2-vdk ;;
  "1-2-mdisks")          ocs-restore-mdisks -um $ocs_user_mode ;;
  "multicast_restoredisk")
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_multicast_restoredisk "$ocs_sr_img_name" "$ocs_sr_dev" "$ocs_sr_mcast_port"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "multicast_restoreparts")
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_multicast_restoreparts "$ocs_sr_img_name" "$ocs_sr_dev" "$ocs_sr_mcast_port"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "bt_restoredisk")
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_bt_restoredisk "$ocs_sr_img_name" "$ocs_sr_dev" "bt"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "bt_restoreparts")
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_bt_restoreparts "$ocs_sr_img_name" "$ocs_sr_dev" "bt"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "ld2d_restoredisk")
    # local device to device (disk to disk)
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_d2d_restoredisk "$ocs_sr_img_name" "$ocs_sr_dev" "ld2d"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "ld2d_restoreparts")
    # local device to device (part to part)
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_d2d_restoreparts "$ocs_sr_img_name" "$ocs_sr_dev" "ld2d"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "nd2d_restoredisk")
    # network device to device (disk to disk)
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_d2d_restoredisk "$ocs_sr_img_name" "$ocs_sr_dev" "nd2d"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  "nd2d_restoreparts")
    # network device to device (part to part)
    task_preprocessing "$ocs_sr_type"
    task_processing_after_parameters_checked

    task_d2d_restoreparts "$ocs_sr_img_name" "$ocs_sr_dev" "nd2d"
    task_postprocessing "$ocs_sr_type" ""
    ;;
  *)
    USAGE
    exit 2
    ;;
esac

save_ocs_related_vars

echo "Ending $0 at $(LC_ALL=C date +%F' '%T' '%Z)..." | tee --append ${OCS_LOGFILE}
exit 0
