# Author: Steven Shiau <steven _at_ nchc org tw>
# License: GPL

###
### functions for drbl-ocs and ocs-onthefly
###
#
min() {
  if [ -n "$1" ] && [ -n "$2"  ]; then
    if [ "$1" -lt  "$2" ]; then
      echo $1;
    else
      echo $2;
    fi
  fi
}
#
get_random_time() {
  local TIME_LIMIT=$1
  local ran0 ran1 ran time
  [ -z "$TIME_LIMIT" ] && TIME_LIMIT="$NOTIFY_OCS_SERVER_TIME_LIMIT"
  # Found ip, ocs_server, mac, we can notify ocs server
  # To avoid all the clients notify at almost same time, we use random sleep before send info.
  # Ref: http://www.faqs.org/docs/abs/HTML/randomvar.html
  # We need extra uniq seed number, otherwise all the clients boot at same time, same boot sqeuence, will have same Unix time, PID, PPID... then the random will be the same. 
  # "ifconfig -a" will give the uniq value, since it contains uniq IP address and MAC address.
  # "$RANDOM" is bash built-in random number, Nominal range: 0 - 32767 (signed 16-bit integer).
  # NOTE: ran0: max 10 digits; ran1: max 5 digits, we want ran1 in the order 0-100, so /1000.0
  ran0="$(ifconfig -a | cksum | cut -f1 -d" ")"
  ran1="$(echo "$ran0 / $RANDOM / 1000.0" | bc -l)"
  ran="$(echo | awk "{ srand($ran1); print rand() }")"
  time="$(echo "scale=0; $TIME_LIMIT * $ran / 1" |bc -l)"
  # in case, if time is empty, give it a value.
  [ -z "$time" ] && time=$TIME_LIMIT
  echo $time
}
#
get_image_cat_zip_cmd() {
  #  cat_prog, zip_stdin_cmd and unzip_stdin_cmd are global variables.
  local imgf="$1"
  if [ -n "$(file $imgf | grep -i "gzip compressed data")" ]; then
    # Ex: hdb1.000: gzip compressed data, from Unix
    # Actually zcat is identical to gunzip -c
    cat_prog="zcat"
    zip_stdin_cmd="gzip -c"
    unzip_stdin_cmd="gunzip -c"
  elif [ -n "$(file $imgf | grep -i "PartImage file .* not compressed")" ]; then
    # Ex: hdb1.000: PartImage file version 0.6.1 volume 0 type reiserfs-3.6 device /dev/hdb1, original filename hdb1, not compressed
    cat_prog="cat"
    zip_stdin_cmd="cat"
    unzip_stdin_cmd="cat"
  elif [ -n "$(echo $imgf | grep -iE "\.ntfs-img$")" -a -n "$(file $imgf | grep -i ": data$")" ]; then
    # Ex: "hda1.ntfs-img: data"
    # This is ntfs-img, uncompressed.
    # Thanks to Guy Carpenter <guyc _at_ atgis com au> for reporting and identifying this bug.
    cat_prog="cat"
    zip_stdin_cmd="cat"
    unzip_stdin_cmd="cat"
  elif [ -n "$(file $imgf | grep -i "bzip2 compressed data")" ]; then
    # Ex: hdb1.000: bzip2 compressed data, block size = 900k
    # Actually bzcat is identical to bzip2 -dc or bunzip2 -c
    cat_prog="bzcat"
    zip_stdin_cmd="bzip2 -c"
    unzip_stdin_cmd="bunzip2 -c"
  elif [ -n "$(file $imgf | grep -i "lzop compressed data")" ]; then
    # Ex: hdb1.ntfs-img: lzop compressed data - version 1.010, LZO1X-1, os: Unix
    cat_prog="lzop -dc"
    zip_stdin_cmd="lzop -c"
    unzip_stdin_cmd="lzop -dc"
  fi
  if [ -z "$cat_prog" ]; then
    echo "Unknown format for $imgf!!! Program terminated!"
    exit 1
  fi
} # end of get_image_cat_zip_cmd
#
conv_return_code_to_human_read() {
  local rcd="$1"
  # clone_status is global variable
  if [ "$rcd" -eq 0 ]; then
    clone_status="success"
  else
    clone_status="***FAIL***"
  fi
} # end of conv_return_code_to_human_read
#
calculate_elapsed_time() {
  local start_t=$1
  local end_t=$2
  # time_elapsed and time_elapsed_in_min are global variable
  time_elapsed="$(LC_ALL=C echo "scale=2; ($end_t - $start_t)/(1*10^9)" | bc -l)"
  time_elapsed_in_min="$(LC_ALL=C echo "scale=3; $time_elapsed / 60 *1.0" | bc -l)"
}
# end of calculate_elapsed_time
#
image_save() {
  local source_dev="$1"
  local target_part_file="$2"
  local image_name_
  local part_fs
  local compress_prog
  local PARTIMAGE_SAVE_OPT_Z0
  # if the report_msg is empty, put the initial one: image_name_
  image_name_="$(dirname $target_part_file)"
  [ -z "$report_msg" ] && report_msg="Saved $image_name_,"
  # time_elapsed, time_elapsed_in_min and speed are global variables
  echo $msg_delimiter_star_line
  echo "Starting saving $source_dev as ${target_part_file}.XXX..."
  # get the filesystem
  part_fs="$($DRBL_SCRIPT_PATH/sbin/get_part_info $source_dev filesystem)"
  if [ "$USE_NTFSCLONE" = "yes" -a "$part_fs" = "ntfs" ]; then
    # case (1): ntfsclone is preferred
    compress_prog_opt="$IMG_CLONE_CMP"
    [ -z "$IMG_CLONE_CMP" ] && compress_prog_opt="gzip -c"
    compress_prog="$(echo "$compress_prog_opt" | sed -e "s/ -.//g")"
    echo "Use ntfsclone with $compress_prog to save the image instead of partimage."
    echo $msg_delimiter_star_line
    ntfs_img_info_tmp="$(mktemp /tmp/ntfs_info.XXXXXX)"
    start_time="$(date +%s%N)"
    ( ntfsclone --save-image --output - $source_dev | \
    $compress_prog_opt > $target_part_file.ntfs-img ) 2>&1 | \
    tee $ntfs_img_info_tmp
    rc="$?"
    end_time="$(date +%s%N)"
    get_ntfs_image_info $ntfs_img_info_tmp $start_time $end_time
    # no preparation time, it's accurate enough.
    echo ">>> Time elapsed: $time_elapsed secs (~ $time_elapsed_in_min mins), average speed: $speed"
    [ -f "$ntfs_img_info_tmp" ] && rm -f $ntfs_img_info_tmp
    # For better security
    chmod 600 $target_part_file.ntfs-img
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $source_dev, $clone_status, $space_used, $time_elapsed_in_min mins, $speed;"
    echo $msg_delimiter_star_line
    echo "Finished saving $source_dev as ${target_part_file}.ntfs-img"
  elif [ -n "$(echo $PARTIMAGE_SAVE_OPT | grep "\-z3")" ]; then
    # case (2): use partimage and stdout pipe to lzop
    # Since partimage does not support lzo, we use pipe to lzop the image.
    # partiamge will use the NO-GUI mode.
    # The basic idea is to use partimage, but compression is made by other program with pipe, so use -z0 in partimage
    # We have to use another variable PARTIMAGE_SAVE_OPT_Z0 to avoid overwrite the original one, otherwise when saving 2nd partition, -z0 will be used, and no compression for 2nd or later partition.
    PARTIMAGE_SAVE_OPT_Z0="$(echo $PARTIMAGE_SAVE_OPT | sed -e "s/-z[0-9]/-z0/g")"
    compress_prog_opt="$IMG_CLONE_CMP"
    # if something went wrong, we convert to gzip.
    if [ -z "$IMG_CLONE_CMP" ] || ! type lzop &>/dev/null; then
      compress_prog_opt="gzip -c"
    fi
    compress_prog="$(echo "$compress_prog_opt" | sed -e "s/ -.//g")"
    echo "Use $compress_prog to compress the image."
    # it is important to run partimage in save mode with volume=0 and -B gui=no
    # so that we can send the data to stdout
    start_time="$(date +%s%N)"
    partimage $DEFAULT_PARTIMAGE_SAVE_OPT $PARTIMAGE_SAVE_OPT_Z0 -B gui=no save $source_dev stdout | $compress_prog_opt > $target_part_file
    rc="$?"
    end_time="$(date +%s%N)"
    calculate_elapsed_time $start_time $end_time
    echo ">>> Time elapsed: $time_elapsed secs (~ $time_elapsed_in_min mins)"
    #
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $source_dev, $clone_status, $time_elapsed_in_min mins;"
    echo $msg_delimiter_star_line
    echo "Finished saving $source_dev as ${target_part_file}.XXX"
  else
    # case (3): use partimage and compress code inside partimage
    start_time="$(date +%s%N)"
    partimage $DEFAULT_PARTIMAGE_SAVE_OPT $PARTIMAGE_SAVE_OPT save $source_dev $target_part_file
    rc="$?"
    end_time="$(date +%s%N)"
    calculate_elapsed_time $start_time $end_time
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $source_dev, $clone_status, $time_elapsed_in_min mins;"
    echo $msg_delimiter_star_line
    echo "Finished saving $source_dev as ${target_part_file}.XXX"
  fi
  echo $msg_delimiter_star_line
  if [ "$rc" -ne 0 ]; then
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "Failed to save partition $source_dev! $msg_press_enter_to_continue..."
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     read
  fi
  return $rc
} # end of image_save

# check if the user input /dev/hda, /dev/hdb...
check_input_hd() {
    local target_hd="$1"
    case "$target_hd" in
	 [hs]d[a-z])
	   true
	   ;;
	 *)
	  echo "\"$target_hd\" is unknown HD device! Known and supported HD device is like hda, hdb, hdc, hdd... Program terminated!"
          echo "Press Enter to exit..."
	  read
	  exit 1
    esac
}
check_input_partition() {
    local tgt_parts="$*"
    for ipart in $tgt_parts; do
      case "$ipart" in
           [hs]d[a-z][0-9]*)
             continue
             ;;
           *)
            echo "\"$target_part\" is unknown partition device! Known and supported partition device is like hda1, hda2, hdb1, hdc1... Program terminated!"
            echo "Press Enter to exit..."
            read
            exit 1
      esac
    done
}
#
check_specify_hd_exists() {
  # the parameter is like hda, sda...
  local check_target_hd=$1
  if [ -z "$check_target_hd" ]; then
    echo "You have to assign a device to be checked!"
    echo "Skip checking HD!!!"
    return 1
  fi
  # Check if the target HD exists or not
  # it's devfs compiled into kernel, we will use the conv_devfspart_to_tradpart to convert the devfs style partitions into traditional partitions
  if grep -Eq '(scsi/host|ide/host)' /proc/partitions 2>/dev/null; then
     # it's devfs compiled into kernel...
     partition_table=$(mktemp /tmp/parttable.XXXXXX)
     conv_devfspart_to_tradpart $partition_table
  else
     # the devfs is not compiled into kernel, good!
     partition_table="/proc/partitions"
  fi
  if [ -z "$(grep -E "$check_target_hd\>" $partition_table)" ]; then
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "Unable to find target HD \"$check_target_hd\"!!!"
     echo "Check if the HD $check_target_hd really exists, otherwise maybe the kernel is too old!"
     echo "Program terminated!!!"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     echo "Press \"Enter\" to exit."
     read
     exit 1
  fi
  [ -f "$partition_table" -a "$partition_table" != "/proc/partitions" ] && rm -f $partition_table
}
#
get_known_partition() {
    local partition_table="$1"
    cat $partition_table | grep "Id=" | awk -F, '{ if($3!=" Id= 0" && $3!=" Id= f" && $3!=" Id=82" && $3!=" Id= 5" && $3!=" Id=85") print $1; }' | cut -d" " -f1 | sed -e 's/\/dev\///g'
}

# check grub partition
check_grub_partition() {
  partition_list=""
  # it's devfs compiled into kernel, we will use the conv_devfspart_to_tradpart to convert the devfs style partitions into traditional partitions
  if grep -Eq '(scsi/host|ide/host)' /proc/partitions 2>/dev/null; then
     # it's devfs compiled into kernel...
     partition_table=$(mktemp /tmp/parttable.XXXXXX)
     conv_devfspart_to_tradpart $partition_table
  else
     # the devfs is not compiled into kernel, good!
     partition_table="/proc/partitions"
  fi
  partition_list=$(LC_ALL=C awk '/[hs]d[a-z][0-9]+/ { print $4; }' $partition_table)
  for ipartition in $partition_list; do
    # if grub_partition is not set, set default
    grub_partition="/dev/$ipartition"
    # process the boot loader
    hd_img=`mktemp -d /tmp/hd_img.XXXXXX`
    mount $grub_partition $hd_img >/dev/null 2>&1
    mrc=$?
    # check if /boot is in its own partition, if so, different mount point.
    # grub root-directory must have "/boot/"
    if [ -d "$hd_img/boot/grub" -o -d "$hd_img/grub/" ]; then
       # Found the partition...
       # remount it as /tmp/hd_img.XXXXXX/boot
       found_grub_partition="$grub_partition"
       [ $mrc -eq 0 ] && umount $grub_partition
       [ -d "$hd_img" ] && rm -rf $hd_img
       [ -d "$hd_img/boot" ] && rm -rf $hd_img/boot
       break;
    fi
    [ $mrc -eq 0 ] && umount $grub_partition
    [ -d "$hd_img" ] && rm -rf $hd_img
    [ -d "$hd_img/boot" ] && rm -rf $hd_img/boot
    [ -f "$partition_table" -a "$partition_table" != "/proc/partitions" ] && rm -f $partition_table
  done
  echo $found_grub_partition
}

# check input_device
check_if_input_device_exist() {
  ANS_TMP=$1
  shift
  local input_dev="$*"
  local partition_table
  [ -z "$input_dev" ] && return 1
  # it's devfs compiled into kernel, we will use the conv_devfspart_to_tradpart to convert the devfs style partitions into traditional partitions
  if grep -Eq '(scsi/host|ide/host)' /proc/partitions 2>/dev/null; then
     # it's devfs compiled into kernel...
     partition_table=$(mktemp /tmp/parttable.XXXXXX)
     conv_devfspart_to_tradpart $partition_table
  else
     # the devfs is not compiled into kernel, good!
     partition_table="/proc/partitions"
  fi

  ret_dev="$input_dev"
  for idev in $input_dev; do
    if [ -z "$(awk -F" " '{print $4}' $partition_table | grep -E "\<$idev\>")" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "The input device [$idev] does NOT exist in this machine!"
      echo "We will not save this device [$idev]!"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      ret_dev="$(echo $iret_dev | sed -e "s/\<$idev\>//g")"
      echo "Press enter to continue!"
      read
    else
      echo "Selected device [$idev] found!"
    fi
  done
  echo $ret_dev > $ANS_TMP
  echo "The selected devices: $ret_dev"
}

# re-install grub
install_grub_hd() {
    local grub_partition="$1"
    # if grub_partition is not set, set default
    if [ "$grub_partition" = "auto" ]; then
       grub_partition="$(check_grub_partition)"
       if [ -n "$grub_partition" ]; then
         echo "Found grub root partition: $grub_partition... Trying to run grub-install now... "
       else
         echo "The grub root directory is NOT found. Maybe it does not exist (so other boot manager exists) or the file system is not supported in the kernel. Skip grub-install." 
	 return 1
       fi
    fi
    # grub_partition is like "/dev/hda1" or "/dev/sda1"
    # So grub_hd is like "hda" or "sda"
    grub_hd="${grub_partition:5:3}"
    # process the boot loader
    hd_img=`mktemp -d /tmp/hd_img.XXXXXX`
    mount $grub_partition $hd_img
    # check if /boot is in its own partition, if so, different mount point.
    # grub root-directory must have "/boot/"
    if [ ! -d "$hd_img/boot/" ]; then
       # In this case, /boot must exist in its own partition, 
       # not same partition with / (root partition).

       # remount it as /tmp/hd_img.XXXXXX/boot
       umount $hd_img
       mkdir -p $hd_img/boot
       mount $grub_partition $hd_img/boot/
       # if device.map exists, remove it to avoid some problem.
       [ -f "$hd_img/boot/grub/device.map" ] && rm -f $hd_img/boot/grub/device.map
       grub-install --root-directory=$hd_img /dev/$grub_hd
       rc=$?
    else
       # if device.map exists, remove it to avoid some problem.
       [ -f "$hd_img/boot/grub/device.map" ] && rm -f $hd_img/boot/grub/device.map
       grub-install --root-directory=$hd_img /dev/$grub_hd
       rc=$?
    fi

    # Check if grub config exists or not
    [ ! -f "$hd_img/boot/grub/menu.lst" -a ! -f "$hd_img/boot/grub/grub.conf" ] && echo "WARNING!!! Can NOT find the grub config file \"menu.lst\" or \"grub.conf\" in the system cloned!!!"

    if [ $rc -gt 0 ]; then
        [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
	echo "Failed to install grub!!!"
        [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
	[ "$debug_mode" = "on" ] && sulogin
    fi
    umount $grub_partition
    [ -d "$hd_img" ] && rm -rf $hd_img
    [ -d "$hd_img/boot" ] && rm -rf $hd_img/boot
    echo "done!"
}

# output the CHS values of a HD
output_HD_CHS() {
   local DEV=$1
   local target_d=$2
   [ -z "$DEV" -o -z "$target_d" ] && return 1
   cylinders="$(sfdisk -g /dev/$DEV |grep -oEi "[[:digit:]]+ cylinders" | awk -F" " '{print $1}')"
   heads="$(sfdisk -g /dev/$DEV |grep -oEi "[[:digit:]]+ heads" | awk -F" " '{print $1}')"
   sectors="$(sfdisk -g /dev/$DEV |grep -oEi "[[:digit:]]+ sectors" | awk -F" " '{print $1}')"
   [ -f "$target_d/chs.sf" ] && rm -f $target_d/chs.sf
   for i in cylinders heads sectors; do
     eval num=\$$i
     echo "$i=$num" >> $target_d/chs.sf
   done
}

# sfdisk failed process
sfdisk_fail_process() {
    echo "sfdisk failed, abort!"
    echo "Please make sure your HD exist or the size of HD is big enough!"
    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
}
#
load_HD_geometry() {
  local target_d=$1
  no_CHS_data_warning() {
      [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
      echo "Unable to find the previous saved file $target_d/chs.sf!"
      echo "Maybe the image is created by old verson clonezilla."
      echo "We will not force to use the specified HD geometry."
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  }

  [ -z "$target_d" ] && return 1
  if [ -f "$target_d/chs.sf" ]; then
    . $target_d/chs.sf
    [ -z "$cylinders" -o -z "$heads" -o -z "$sectors" ] && no_CHS_data_warning
    sfdisk_opt="$sfdisk_opt -C $cylinders -H $heads -S $sectors"
  else 
    no_CHS_data_warning
  fi
}
#
clean_cylinder_boundary_warning() {
  # We have to clean this two warnings, otherwise sfdisk will refuce to write
  # even if we run something like:
  # sfdisk --force -C# -H# -S# /dev/hda < pt.sf...
  local part_file=$1
  if [ -f "$part_file" ]; then
    perl -p -i -e "s/^Warning: extended partition does not start at a cylinder boundary.//gi" $part_file
    perl -p -i -e "s/^DOS and Linux will interpret the contents differently.//gi" $part_file
  fi
}
# create partition from file
create_partition() {
  # tgt_hd: /dev/hda, /dev/hdb...
  # create_partition_opt is global variable
  local tgt_hd="$1"
  local tgt_dir="$2"
  local sf_opt="$3"
  # turn off the swap partition and file to unlock the busy partition
  if [ -e /etc/debian_version -o -e /etc/SuSE-release ]; then
    # Debian or SUSE
    # just stop it!
    /etc/init.d/mkswapfile stop
  else
    # RH-like
    [ -f /var/lock/subsys/mkswapfile ] && /etc/init.d/mkswapfile stop
  fi
  /sbin/swapoff -a
  # Stop LVM so that the HD will not be occupied, we use script comes from
  # gentoo, but actually just run "vgchange -an" will be ok.
  $DRBL_SCRIPT_PATH/sbin/lvm2-stop.sh

  # create partition from file
  if [ "$create_part" = "yes" ]; then
    # Before create partition, clean the warning message in pt.sf so that sfdisk
    # will not refuse to do that.
    echo $msg_delimiter_star_line
    date | tee $RESTORE_SFDISK_LOG
    echo "Writinging the partition table..." | tee -a $RESTORE_SFDISK_LOG
    # dump partition table only without running sfdisk
    # ref: http://www.nautilus.com.br/~rei/material/artigos/sistema-de-boot.html
    case "$create_part_by_sfdisk" in
      n|N|[nN][oO])
        dd if=$tgt_dir/mbr of=$tgt_hd skip=446 seek=446 bs=1 count=66
        RETVAL="$?"
	if [ "$RETVAL" -eq 0 ]; then
	  echo -n "Making kernel re-read the partition table of $tgt_hd... "
	  sfdisk -R $tgt_hd
	  echo "done!"
          echo "The partition table of $tgt_hd is:"
          fdisk -l $tgt_hd
	else
          [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
	  echo "Unable to use dd to dump the partition table in $tgt_hd!"
	  echo "Something weng wrong! The clone maybe won't work!"
          [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
	fi
        ;;
      y|Y|[yY][eE][sS])
        # create partition table by running sfdisk
        echo "Running sfdisk $sf_opt $tgt_hd < $tgt_dir/pt.sf" | tee -a $RESTORE_SFDISK_LOG
        sfdisk $sf_opt $tgt_hd < $tgt_dir/pt.sf | tee -a $RESTORE_SFDISK_LOG
        RETVAL="$?"
        echo "This is done by sfdisk $sf_opt $tgt_hd < $tgt_dir/pt.sf" | tee -a $RESTORE_SFDISK_LOG
        # This is useless if $sf_opt (--force is used)! Since we always use --force now, so the return value is "0"
        if [ $RETVAL -ne 0 ]; then
          sfdisk_fail_process
          exit 1
        fi
        ;;
    esac
  fi
} # end of create_partition
#
task_preprocessing() {
  local mode="$1"
  local server_tmp=""
  local mntpnt_tmp=""

  # check if the kernel cmdline wants this client to abort or not...
  check_boot_kernel_arg_cmdline

  # mount_point
  if [ -n "$mount_point" ]; then
    if [ -n "$(echo "$mount_point" | grep ":")" ]; then
      server_tmp="$(echo "$mount_point" | awk -F":" '{print $1}')"  
      mntpnt_tmp="$(echo "$mount_point" | awk -F":" '{print $2}')"
    else
      server_tmp=$(awk '$2 ~ /\/$/ {print $1}' /etc/fstab | awk -F ":" '{print $1}')
      mntpnt_tmp=$mount_point
    fi
    mount -t nfs $server_tmp:$mntpnt_tmp $ocsroot
    if [ $? -ne 0 ]; then
      echo "The mount point you specified ($mntpnt_tmp) is not exported by server ($server)"
      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]) /sbin/sulogin ;;
         *) task_postprocessing ;;
      esac
    fi
  fi

  # wait for some commands if specified by user
  if [ -n "$TIME_to_wait" ]; then
    n="$TIME_to_wait"
    echo "Last chance to quit in $TIME_to_wait seconds...Shutdown the machine to avoid continuing."
    until [ $n -le 1 ]; do n=`expr $n - 1`; echo -en "$n\b"; sleep 1; done
  fi
  if [ "$confirm_restore" = "on" -a -n "$(echo $mode | grep restore)" ]; then
    echo "The following step is to restore the image to your harddisk."
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "WARNING!!! WARNING!!! WARNING!!!"
    echo "WARNING! The existing data in the partition/harddisk will be overwritten! ALL DATA WILL BE LOST!!!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo -n "Are you sure you want to continue ? (y/N) "
    read restore_confirm_ans
    case "$restore_confirm_ans" in
         y|Y|[yY][eE][sS])
            echo "Let's do it!"
            ;;
         *)
            echo "Program terminated!"
            exit 1
   esac
  fi
  if [ "$debug_mode" = "on" ]; then
    echo "Enter shell...Use \"exit\" to back to the original program."
    sulogin
  fi
}
#
convert_post_run_cmd() {
  local_post_run="$1"
  case "$local_post_run" in
       poweroff)
  	   local_post_run="sync;sync;sync; /sbin/poweroff -f -n"
  	   ;;
       command)
           # add reboot following the sulogin, otherwise the rc1.d will loop
  	   # forever if user just exit sulogin.
  	   local_post_run="/sbin/sulogin; /sbin/reboot -f -n"
  	   ;;
       reboot)
  	   local_post_run="sync;sync;sync; /sbin/reboot -f -n"
  	   ;;
  	   *)
           # append sync
  	   local_post_run="sync;sync;sync; $local_post_run"
  	   ;;
  esac
  echo "$local_post_run"
}

# task_postprocessing
task_postprocessing() {
  # option_mount_point
  if [ -n "$mount_point" ]; then
    umount $ocsroot
    #[ $mk_ocsroot -eq 1 ] && rmdir $ocsroot
  fi
  # option debug
  if [ "$debug_mode" = "on" ]; then
    echo "Enter shell...Use \"exit\" to back to the original program."
    sulogin
  fi
}

# resize partition
resize_partition() {
   # Input: harddisk_name 
   # Input example: hda
   # Steven TODO if partition ($2) is specified, resize that partition only
   local HD="$1"
   local part_info_tmp=
   local part_to_resize=
   HD_DEV="/dev/$HD"
   # find the known filesystem
   part_info_tmp=`mktemp /tmp/part.XXXXXX`
   LC_ALL=C parted -s $HD_DEV p > $part_info_tmp
   known_fs="fat16 fat32 vfat ext2 ext3 reiserfs ntfs"
   for ifs in $known_fs; do
     if  [ -n "`grep $ifs $part_info_tmp`" ]; then
	 part_no=$(grep $ifs $part_info_tmp | awk -F" " '{print $1}')
         part_to_resize="$part_to_resize $part_no"
     fi
   done
   echo "The partition to be resized in harddrive $HD : $part_to_resize"

   for ipart in $part_to_resize; do
      echo "Now resize the partition for $HD$ipart"
      $DRBL_SCRIPT_PATH/sbin/resize_part $HD $ipart
   done

   [ -f "$part_info_tmp" ] && rm -f $part_info_tmp
}

ask_time_or_clients_to_wait() {
      local n_clients_tmp
      local time_opt_tmp
      # Note!!! We must use common tmp file to pass the result, we can not
      # just echo result in the end of this function. Otherwise in newer
      # dialog (FC3), the script will wait for the end of function, then it 
      # shows the result. 
      # There is nothing in the screen when function is called if we just 
      # use echo the result to stdout.
      ANS_TMP=$1
      TMP=`mktemp /tmp/ocs.XXXXXX`
      trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
      n_clients_tmp=0
      time_opt_tmp=0
      n_clients_tmp_default="$($DRBL_SCRIPT_PATH/bin/get-client-ip-list | wc -l | sed -e "s/ //g")"
      $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
      --radiolist "$msg_choose_the_multicast_method:" 0 0 0 \
      "clients+time-to-wait" "$msg_clients_time_to_wait" on \
      "time-to-wait" "$msg_time_to_wait" off \
      "clients-to-wait" "$msg_clients_to_wait" off \
      2> $TMP
      M="$(cat $TMP)"
      case "$M" in
        "time-to-wait") 
           $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
           --inputbox "$msg_time_to_wait_sec ?" 0 0 "$TIME_TO_WAIT_DEFAULT" 2> $TMP
           time_opt_tmp="$(cat $TMP)"
           [ -z "$time_opt_tmp" ] && echo "You must specify the time! Program terminated!" && exit 1
           [ $time_opt_tmp -le 0 ] && echo "Time number can NOT <= 0! Program terminated!!!" && exit 1
           echo "time_to_wait=$time_opt_tmp" > $ANS_TMP
           ;;
        "clients-to-wait")
           ## ask how many clients to restore
           $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
           --inputbox "$msg_how_many_clients_to_restore ?" 0 0 "$n_clients_tmp_default" 2> $TMP
           n_clients_tmp=$(cat $TMP)
           # check client no.
           # Steven TODO, better to re-ask if n_client <=0
           [ -z "$n_clients_tmp" ] && echo "You must specify the client number! Program terminated!" && exit 1
           [ $n_clients_tmp -le 0 ] && echo "Client number can NOT <= 0! Program terminated!!!" && exit 1
           echo "clients_to_wait=$n_clients_tmp" > $ANS_TMP
           ;;
        "clients+time-to-wait")
           ## ask how many clients to restore
           $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
           --inputbox "$msg_how_many_clients_to_restore ?" 0 0 "$n_clients_tmp_default" 2> $TMP
           n_clients_tmp=$(cat $TMP)
           # check client no.
           # Steven TODO, better to re-ask if n_client <=0
           [ -z "$n_clients_tmp" ] && echo "You must specify the client number! Program terminated!" && exit 1
           [ $n_clients_tmp -le 0 ] && echo "Client number can NOT <= 0! Program terminated!!!" && exit 1
           # ask time
           $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
           --inputbox "$msg_max_time_to_wait_sec" 0 0 "$MAX_TIME_TO_WAIT_DEFAULT" 2> $TMP
           max_time_opt_tmp="$(cat $TMP)"
           [ -z "$max_time_opt_tmp" ] && echo "You must specify the time! Program terminated!" && exit 1
           [ $max_time_opt_tmp -le 0 ] && echo "Time number can NOT <= 0! Program terminated!!!" && exit 1
           echo "clients_to_wait=$n_clients_tmp" > $ANS_TMP
           echo "max_time_to_wait=$max_time_opt_tmp" >> $ANS_TMP
           ;;
        *)
           echo "You must specify the mode! Program terminated!" && exit 1
      esac 
      [ -f "$TMP" ] && rm -f $TMP
}

#
check_existing_target_image() {
  local tgt_image="$1"
  if [ "$tgt_image" = "$ocsroot" -o -z "$tgt_image" ]; then
    echo "Something went wrong! Contact DRBL developers to fix this problem!"
    echo "Program terminated!!!"
    exit 1
  else
    [ -f "$tgt_image" ] && rm -f $tgt_image
    [ -d "$tgt_image" ] && rm -rf $tgt_image
  fi
}
check_input_target_image() {
  local tgt_dir="$1"
  if [ ! -d "$tgt_dir" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "The directory $tgt_dir for the inputed name do NOT exist!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "Program terminated!!!"
    exit 1
  fi
}

#
get_input_image_name() {
    # parameters: [/home/partimag|...] [hda1|hda2...]
    # return the image name
    # Note!!! We must use common tmp file to pass the result, we can not
    # just echo result in the end of this function. Otherwise in newer
    # dialog (FC3), the script will wait for the end of function, then it 
    # shows the result. 
    # There is nothing in the screen when function is called if we just 
    # use echo the result to stdout.
    ANS_TMP="$1"
    local tgt_name_default
    local ASK_IMGNAME=1
    TMP=`mktemp /tmp/ocs.XXXXXX`
    trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
    while [ "$ASK_IMGNAME" -ne 0 ]; do
      tgt_name_default="$(date +%F-%H-img)"
      $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
         --inputbox "$msg_input_name_to_save_the_img $tgt_name" 0 0 "$tgt_name_default" 2> $TMP
      tgt_img_name="$(cat $TMP)"
      if [ -n "$(echo $tgt_img_name | grep -Eo "[^[:alnum:]_\-]")" ]; then
        # we just accept those image name is alpha, digit, _ and -
        $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --msgbox "$msg_you_must_input_legal_filename!\n$msg_please_do_it_again!" 0 0 
      elif [ -z "$tgt_img_name" ]; then
        $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --msgbox "$msg_you_must_input_filename!\n$msg_please_do_it_again!" 0 0 
      elif [ -d "$ocsroot/$tgt_img_name" ]; then
        $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --yesno "$msg_the_image \"$tgt_img_name\" $msg_was_saved_before!\n\n$msg_do_you_want_to_replace ?" 0 0 2> $TMP
        ASK_IMGNAME=$?
      else
        ASK_IMGNAME=0
      fi
    done
    [ -f "$TMP" ] && rm -f $TMP
    # return the valule
    echo $tgt_img_name > $ANS_TMP
} # end of get_input_image_name
#
get_input_dev_name() {
    # input param, [harddisk|partition]
    # To specify it's hardisk or partition
    # return the hardisks or partitions
    # Note!!! We must use common tmp file to pass the result, we can not
    # just echo result in the end of this function. Otherwise in newer
    # dialog (FC3), the script will wait for the end of function, then it 
    # shows the result. 
    # There is nothing in the screen when function is called if we just 
    # use echo the result to stdout.
    ANS_TMP="$1"
    local dev_list=""
    local HARDDEVS=""
    local partition_table=""
    local selection=""
    local target_dev=""
    local dev_chosen_def=""
    local NUMDEV=0
    local dev_type="$2"

    [ -z "$dev_type" ] && echo "You must specify the type get be entered!!! Program terminated!" && exit 1

    # it's devfs compiled into kernel, we will use the conv_devfspart_to_tradpart to convert the devfs style partitions into traditional partitions
    if grep -Eq '(scsi/host|ide/host)' /proc/partitions 2>/dev/null; then
       # it's devfs compiled into kernel...
       partition_table=$(mktemp /tmp/parttable.XXXXXX)
       conv_devfspart_to_tradpart $partition_table
    else
       # the devfs is not compiled into kernel, good!
       partition_table="/proc/partitions"
    fi

    case "$dev_type" in
      harddisk)
        # when input harddisk, only one selection
        selection="radiolist"
        dev_list=$(LC_ALL=C awk '/[hs]d[a-z]($| )/ { print $4; }' $partition_table | sort)
        NUMDEV="$(echo $dev_list | wc -w)"
        ;;
      partition)
        # file -Ls /dev/hdax output example:
        # /dev/hda5: Linux/i386 swap file (new style) 1 (4K pages) size 113895 pages
        # /dev/hda2: x86 boot sector, extended partition table
        # when input partitions, not only one selection
        selection="checklist"
        dev_list=$(LC_ALL=C awk '/[hs]d[a-z][0-9]+($| )/ { print $4; }' $partition_table | sort)
        for p in $dev_list; do
          # skip swap and extended partition
          FILE="$(LC_ALL=C file -Ls "/dev/$p" | cut -d: -f2 | tr ' ' _ | sed -e "s/^_//g")"
          [ "$?" = "0" ] && case "$FILE" in *[Ss][Ww][Aa][Pp]*|*extended*) continue ;; esac
          NUMDEV=$[NUMDEV+1]
        done
        ;;
    esac
    TMP=`mktemp /tmp/ocs.XXXXXX`
    trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
    # check if any harddisk is found ?
    if [ $NUMDEV -le 0 ]; then
       [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
       echo "Error! Can NOT find any local $dev_type in this system!"
       echo "Press Enter to exit..."
       read
       [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
       exit 1
    fi

    # prepare the dialog menu info
    for p in $dev_list; do
       DEV_MODEL=""
       case "$p" in
          hd[a-z])
            if [ "`cat /proc/ide/$p/media`" = "disk" ] ; then
              DEV_MODEL=`tr ' ' _ </proc/ide/$p/model`
            fi
            ;;
          sd[a-z])
            x="`/sbin/scsi_info_drbl /dev/$p | grep MODEL | tr ' ' _`"
            # the result is like MODEL="ATA_HDS722516VLSA80"
            x=${x#*\"}
            x=${x%\"*}
            DEV_MODEL=${x}
            ;;
          [hs]d[a-z][0-9]*)
            # Skip swap and extended partition
            # We do not use the output of fdisk -l, it's type mabe like
            # Extended, Ext'd, it's not easy to tell... We use file to get the
            # partition type
            part_type="$(LC_ALL=C file -Ls "/dev/$p")"
            rc=$?
            [ "$rc" -gt 0 ] && continue 
            case "$part_type" in *[Ss][Ww][Aa][Pp]*|*extended*) continue ;; esac
            DEV_MODEL="$($DRBL_SCRIPT_PATH/sbin/get_part_info /dev/$p filesystem)"
            ;;
        esac
        [ -z "$DEV_MODEL" ] && DEV_MODEL="No_description"
	# If only 1 device, make it on as default, else let user choose (off).
	if [ "$NUMDEV" = 1 ]; then
	  dev_chosen_def="on"
	else
	  dev_chosen_def="off"
	fi
        HARDDEVS="$HARDDEVS $p $DEV_MODEL $dev_chosen_def"
    done
    $DIA --backtitle "$backtitle" --title "$title" \
      --$selection "$msg_choose_the $dev_type $msg_to_save:" 0 0 $NUMDEV $HARDDEVS \
    2> $TMP
    target_dev=$(cat $TMP)
    [ -f "$TMP" ] && rm -f $TMP 
    [ -f "$partition_table" -a "$partition_table" != "/proc/partitions" ] && rm -f $partition_table

    # return the value
    echo $target_dev > $ANS_TMP
}

# ********************
# NOTE! This get_existing_hda1_image function is abandoned since clonezilla 1.4.0-1, since
# ********************
get_existing_hda1_image() {
   # NOTE! This function is abandoned since clonezilla 1.4.0-1, since
   # we can use partitions to save and restore, no more specific hda1.
   # show existing images so that user can choose then return chosen to file ANS_TMP
   local ANS_TMP=$1
   local tgt_file=
   local TMP=`mktemp /tmp/ocs.XXXXXX`
   trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
   local OLDVERTMP=`mktemp /tmp/oldocs.XXXXXX`
   trap "[ -f "$OLDVERTMP" ] && rm -f $OLDVERTMP" HUP INT QUIT TERM EXIT
   filelist=""
   numfiles=0
   if [ -z "$(ls -d $imagedir/*.000 2> /dev/null)" ]; then
     echo "No Image in $imagedir .. (abort)"; exit 0
   fi
   for file in $imagedir/*.000; do               
     [ "$file" = "$imagedir/ocsmgrd" ] && continue
     [ "$file" = "$imagedir/size" ] && continue
   
     ## Blake, 2005/01/20, backward compability ..
     if [ ! -d $file ]; then
       imgf="${file/\.000/}"
       for imgff in $imgf.*; do
         sub="${imgff##*\.}"
         echo "$imgff $file/hda1.$sub" >> $OLDVERTMP
       done
       fileinfo=`ls -lh $file | awk '{ print $6"_"$7"_"$5; }'`
     else
       fileinfo=`ls -lh $file/hda1.000 | awk '{ print $6"_"$7"_"$5; }'`
     fi
     filename=${file##/*/}
     filename=${file##*/}
     filelist="$filelist $filename $fileinfo off"
     echo "$numfiles. $filename $fileinfo" >> $TMP
     numfiles=`expr $numfiles + 1`
   done
   ## Blake, 2005/01/20, backward compability
   if [ "$(wc -l $OLDVERTMP | awk -F " " '{print $1}')" != "0" ]; then
     echo "To make it compatble with the new clonezilla,"
     echo "we have to move some of the old partimage files into directories."
     echo "The following partimage files are moving:"
     while read from to; do echo "  $from -> $to"; done < $OLDVERTMP
     echo "Press any key to continue..."
     read anykey
     while read from to; do
       filename="${from##*/}"
       dirname="${filename/\.[0-9][0-9][0-9]/.000}"
       size="$(grep "^$filename" $imagedir/size | cut -d: -f2)"
       mv $from $imagedir/$filename.$$
       [ ! -d $imagedir/$dirname ] && mkdir -p $imagedir/$dirname
       mv $imagedir/$filename.$$ $to
       filename="${to##*/}"
       echo "$filename:$size" >> $imagedir/$dirname/size
     done < $OLDVERTMP
   fi
   
   # Choose tgt_file
   if [ $numfiles -gt 0 ]; then
     if [ $numfiles -lt $MAX_DIALOG_HEIGHT ]; then
       height=$numfiles
     else
       height="$MAX_DIALOG_HEIGHT"
     fi
     $DIA \
       --backtitle "$msg_nchc_free_software_labs" \
       --title "$msg_nchc_clonezilla" \
       --radiolist "$msg_choose_the_image_to_restore:" 0 $RESTORE_DIA_WIDTH \
       $height $filelist 2> $TMP
     tgt_file=$(cat $TMP)
   fi
   [ -f "$TMP" ] && rm -f $TMP
   [ -f "$OLDVERTMP" ] && rm -f $OLDVERTMP
   # return the valule
   echo $tgt_file > $ANS_TMP
}
#
get_existing_disk_image() {
   # show existing images so that user can choose then return chosen to file ANS_TMP
   local ANS_TMP=$1
   local tgt_file=
   local ASK_IMGNAME=1
   local TMP=`mktemp /tmp/ocs.XXXXXX`
   trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
   numfiles=`ls $imagedir 2> /dev/null | wc -l`
   numfiles=`expr $numfiles + 0`
   # list the previous saved images
   # the list of images can be put in one page
   filelist=""
   numfiles=0
   for file in `ls $imagedir 2> /dev/null`; do
     # only directory ..., not file
     [ ! -d "$imagedir/$file" ] && continue
     [ ! -f "$imagedir/$file/disk" ] && continue
     fileinfo=`ls -lh $imagedir/$file/disk 2>/dev/null| awk '{ print $6"_"$7; }'`
     fileinfo=$fileinfo"_"$(cat $imagedir/$file/disk 2>/dev/null)
     filelist="$filelist $file $fileinfo off"
     numfiles=`expr $numfiles + 1`
   done

   if [ $numfiles -gt 0 ]; then
     if [ $numfiles -lt $MAX_DIALOG_HEIGHT ]; then
       height="$numfiles"
     else
       height="$MAX_DIALOG_HEIGHT"
     fi
     if [ "$numfiles" = "1" ]; then
       # If only one image, turn on it.
       filelist="$(echo $filelist | sed -e "s/off$/on/g")"
     fi
     while [ "$ASK_IMGNAME" -ne 0 ]; do
       $DIA \
         --backtitle "$msg_nchc_free_software_labs" \
         --title "$msg_nchc_clonezilla" \
         --radiolist "$msg_choose_the_image_to_restore:" 0 $RESTORE_DIA_WIDTH \
         $height $filelist 2> $TMP
       tgt_file="$(cat $TMP)"
       if [ -z "$tgt_file" ]; then
         $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --msgbox "$msg_you_must_input_filename!\n$msg_please_do_it_again!" 0 0 
       else
         ASK_IMGNAME=0
       fi
     done
   else
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "No Image is found in $imagedir.. Make sure you already saved an image! Program terminated!!!"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     exit 1
   fi
   [ -f "$TMP" ] && rm -f $TMP
   # return the valule
   echo $tgt_file > $ANS_TMP
} # end of get_existing_disk_image

#
get_existing_parts_image() {
   # show existing partitions images so that user can choose then return chosen to file ANS_TMP
   local ANS_TMP=$1
   local tgt_file
   local ASK_IMGNAME=1
   local TMP=`mktemp /tmp/ocs.XXXXXX`
   trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
   numfiles=`ls $imagedir 2> /dev/null | wc -l`
   numfiles=`expr $numfiles + 0`
   filelist=""
   numfiles=0
   for file in `ls $imagedir 2> /dev/null`; do
     # only directory ..., not file
     [ ! -d "$imagedir/$file" ] && continue
     [ ! -f "$imagedir/$file/parts" -a ! -f "$imagedir/$file/disk" ] && continue
     fileinfo=`ls -lhd $imagedir/$file | awk '{ print $6"_"$7; }'`
     if [ -f "$imagedir/$file/parts" ]; then 
       fileinfo=$fileinfo"_"$(tr ' ' _ < $imagedir/$file/parts)
     elif [ -f "$imagedir/$file/disk" ]; then 
       fileinfo=$fileinfo"_"$(cat $imagedir/$file/disk)
     fi
     filelist="$filelist $file $fileinfo off"
     numfiles=`expr $numfiles + 1`
   done
   if [ $numfiles -gt 0 ]; then
     if [ $numfiles -lt $MAX_DIALOG_HEIGHT ]; then
       height="$numfiles"
     else
       height="$MAX_DIALOG_HEIGHT"
     fi
     if [ "$numfiles" = "1" ]; then
       # If only one image, turn on it.
       filelist="$(echo $filelist | sed -e "s/off$/on/g")"
     fi
     while [ "$ASK_IMGNAME" -ne 0 ]; do
       $DIA \
       --backtitle "$msg_nchc_free_software_labs" \
       --title "$msg_nchc_clonezilla" \
       --radiolist "$msg_choose_the_image_to_restore:" 0 $RESTORE_DIA_WIDTH \
       $height $filelist 2> $TMP
       tgt_file="$(cat $TMP)"
       if [ -z "$tgt_file" ]; then
         $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --msgbox "$msg_you_must_input_filename!\n$msg_please_do_it_again!" 0 0 
       else
         ASK_IMGNAME=0
       fi
     done
   else
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "No image directory is found in $imagedir! Program terminated!!!"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     exit 1
   fi
   [ -f "$TMP" ] && rm -f $TMP
   # return the valule
   echo $tgt_file > $ANS_TMP
} # end of get_existing_parts_image

#
get_existing_partitions_from_img() {
   local ANS_TMP=$1
   local tgt_file=$2
   local tgt_part
   local ASK_IMGNAME=1
   # If the image $tgt_file is created by savedisk, list partitions info now
   if [ ! -f "$tgt_file/parts" -a -f "$tgt_file/pt.sf" ]; then
     for part in `get_known_partition $tgt_file/pt.sf`; do
       echo -n "$part " >> $tgt_file/parts
     done
   fi
   #
   NUMPARTS="$(wc -w $tgt_file/parts 2>/dev/null | awk '{print $1}')"
   if [ -n "$NUMPARTS" -a "$NUMPARTS" -gt 0 ]; then
     if [ $NUMPARTS -lt $MAX_DIALOG_HEIGHT ]; then
       height="$NUMPARTS"
     else
       height="$MAX_DIALOG_HEIGHT"
     fi
     TMP=`mktemp /tmp/ocs.XXXXXX`
     trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
     PROMPT="$(for p in `cat $tgt_file/parts` ; do echo "$p" "disk("${p:0:3}")_partition("${p:3}")" off ; done)"
     if [ "$NUMPARTS" = "1" ]; then
       # If only one dev, turn on it.
       PROMPT="$(echo $PROMPT | sed -e "s/off$/on/g")"
     fi
     while [ "$ASK_IMGNAME" -ne 0 ]; do
       $DIA --backtitle "$msg_nchc_free_software_labs" --title  \
       "$msg_nchc_clonezilla" --checklist "$msg_choose_the_parts_to_restore:" \
       0 0 $height $PROMPT 2> $TMP
       tgt_parts="$(cat $TMP)"
       if [ -z "$tgt_parts" ]; then
         $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --msgbox "$msg_you_must_input_partition!\n$msg_please_do_it_again!" 0 0 
       else
         ASK_IMGNAME=0
       fi
     done
   else
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "No partition image file is found in $tgt_file! Program terminated!!!"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     exit 1
   fi
   [ -f "$TMP" ] && rm -f $TMP
   # return the valule
   echo $tgt_parts > $ANS_TMP
} # end of get_existing_partitions_from_img

#
check_dhcpd_config() {
    ## check dhcp server
    if [ ! -e $DHCPDCONF_DIR/dhcpd.conf ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "You must setup DRBL first. Please see http://drbl.nchc.org.tw or http://drbl.sf.net. Abort!"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      exit 1
    fi
}
check_dhcpd_if_range() {
  # Warning if range option is used in /etc/dhcpd.conf
  grep "^[ +]*[^#][ *]range" $DHCPDCONF_DIR/dhcpd.conf > /dev/null 2>&1 
  RETVAL=$?
  if [ $RETVAL -eq 0 ]; then
    # found the range option, which is not comment
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$msg_range_found_in_dhcpd_conf"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    if [ "$batch_mode" != "on" ]; then
      echo -n "$msg_press_enter_to_continue..."
      read
    fi
  fi
}
#
force_pxe_clients_boot_drbl() {
  local menu_label="$1"
  echo "Setting the PXE clients to remote boot linux with label \"$menu_label\"..."
  $DRBL_SCRIPT_PATH/sbin/set-default-pxe-img -i drbl -c $PXE_CONF -l "$menu_label"
  # specify the nodes if assigned by user
  [ "$LIST_HOST" = "on" ] && set_specific_host_pxe_conf $IP_LIST
}
#
force_pxe_clients_boot_local() {
  # force to make local boot without PXE passwd checking
  lines=$(get_pxecfg_image_block local $PXE_CONF)
  begin_line=$(echo $lines | awk -F" " '{print $1}')
  end_line=$(echo $lines | awk -F" " '{print $2}')
  sub_cmd="if ($begin_line..$end_line) {s/^[[:space:]]*[#]*[[:space:]]*(MENU PASSWD.*)/  # drbl-ocs REMOVE PASSWD forever since always-restore mode is set!/gi}"
  perl -pi -e "$sub_cmd" $PXE_CONF
  echo "Setting the PXE clients to local boot..."
  # make it use default one
  $DRBL_SCRIPT_PATH/sbin/set-default-pxe-img -i local -c $PXE_CONF
  # specify the nodes if assigned by user
  [ "$LIST_HOST" = "on" ] && set_specific_host_pxe_conf $IP_LIST
}
#
do_nfs_restart() {
      [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
      echo "Restart nfs server to make sure the stalled NFS is cleaned..."
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      /etc/init.d/$NFS_SRV_NAME restart
}
#
find_multicast_ethernet_port() {
    echo -n "Finding the multicast seed ethernet port... "
    for eth in `get_dhcpd_interface`; do
      # keep the port for multicast to be echoed in screen
      eth_for_multicast="$eth"
      break
    done
    echo "done."
    echo "Will use ethernet port $eth_for_multicast for multicast seed in this clonezilla server."
} # end of find_multicast_ethernet_port
# get ntfs restore statistics
get_ntfs_image_info() {
  local report_info=$1
  local start_t=$2
  local end_t=$3
  local space_used_number space_used_unit
  # time_elapsed, time_elapsed_in_min, space_used and speed are global variables
  [ ! -e "$report_info" -o -z "$start_t" -o -z "$end_t" ] && return 1
  # The report of ntfsclone is like:
  # Space in use       : 7 MB (0.5%)
  calculate_elapsed_time $start_t $end_t
  space_used="$(awk -F":" '/^Space in use/ {print $2}' $report_info | sed -e "s/[[:space:]]*([[:digit:]]*.*)[[:space:]]*//g" | sed -e "s/^[[:space:]]*//g")"
  space_used_number="$(echo $space_used | awk -F" " '{print $1}')"
  space_used_unit="$(echo $space_used | awk -F" " '{print $2}')"
  speed="$(LC_ALL=C echo "scale=1; $space_used_number / $time_elapsed *60.0" | bc -l)"
  # show it with unit
  speed="$speed $space_used_unit/min"
  return 0
}
# Since there is a bug in partimage for split image files,
# http://www.partimage.org/forums/viewtopic.php?t=363
# Use stdin input for restoring is a good solution.
do_unicast_stdin_restore() {
  # part is like: /dev/hda1
  local target_d="$1"
  local img_file="$2"
  local part="$3"
  local start_time end_time image_name_
  # if the report_msg is empty, put the initial one: image_name_
  image_name_="$(basename $target_d)"
  [ -z "$report_msg" ] && report_msg="Unicast restored $image_name_,"
  # time_elapsed, time_elapsed_in_min and speed are global variables
  # get the cat program: cat, zcat or bzcat
  if [ -f "$target_d/$img_file.000" -o -f "$target_d/$img_file" ]; then
    # The saved image is from partimage
    if [ -f "$target_d/$img_file.000" ]; then
      get_image_cat_zip_cmd $target_d/$img_file.000
      # The files are split, like hda1.000, hda1.001, so we have to add "."
      img_file_prefix="$img_file.*"
    else
      get_image_cat_zip_cmd $target_d/$img_file
      # The file is NOT split, so the file name is just like "hda1" only, no "."
      img_file_prefix="$img_file"
    fi
    echo $msg_delimiter_star_line
    echo "Starting unicast restoring image ${target_d##*/} to $part..."
    start_time="$(date +%s%N)"
    toskip=0
    ( for img in $target_d/$img_file_prefix; do
        $cat_prog $img | dd bs=512 skip=$toskip 2> /dev/null
        toskip=1
      done
    ) | partimage $DEFAULT_PARTIMAGE_RESTORE_OPT $PARTIMAGE_RESTORE_OPT -o -d restore $part stdin
    # TODO
    # partimage will return 1 no matter it finishes or not when we use stdin and
    # other options to suppress the warning message... 
    # So just return 0.
    rc=0
    end_time="$(date +%s%N)"
    calculate_elapsed_time $start_time $end_time
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $part, $clone_status, $time_elapsed_in_min mins;"
  elif [ -f "$target_d/$img_file.ntfs-img" ]; then
    # The saved image is from ntfsclone
    get_image_cat_zip_cmd $target_d/$img_file.ntfs-img
    echo $msg_delimiter_star_line
    echo "Starting unicast restoring image ${target_d##*/} to $part..."
    echo $msg_delimiter_star_line
    ntfs_img_info_tmp="$(mktemp /tmp/ntfs_info.XXXXXX)"
    start_time="$(date +%s%N)"
    $unzip_stdin_cmd $target_d/$img_file.ntfs-img | \
    ntfsclone --restore-image --overwrite $part - | tee $ntfs_img_info_tmp
    rc="$?"
    end_time="$(date +%s%N)"
    get_ntfs_image_info $ntfs_img_info_tmp $start_time $end_time
    # For unicast, no preparation time, it's accurate enough.
    echo ">>> Time elapsed: $time_elapsed secs (~ $time_elapsed_in_min mins), average speed: $speed"
    [ -f "$ntfs_img_info_tmp" ] && rm -f $ntfs_img_info_tmp
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $part, $clone_status, $space_used, $time_elapsed_in_min mins, $speed;"
  fi
  echo "Finished unicast restoring image ${target_d##*/} to $part."
  echo $msg_delimiter_star_line
  return $rc
} # end of do_unicast_stdin_restore
#
do_multicast_udpcast_restore() {
  # part is like: /dev/hda1
  # port is global variable
  # time_elapsed, time_elapsed_in_min and speed are global variables
  local target_d="$1"
  local img_file="$2"
  local part="$3"
  local start_time end_time time_limit time image_name_
  # if the report_msg is empty, put the initial one: image_name_
  image_name_="$(basename $target_d)"
  [ -z "$report_msg" ] && report_msg="Multicast restored $image_name_,"
  udpcast_rec_cmd="udp-receiver --nokbd --mcast-all-addr $MULTICAST_ALL_ADDR --portbase $port $TIME_TO_LIVE_OPT" 
  echo $msg_delimiter_star_line
  echo "Starting multicast restoring image ${target_d##*/} to $part..."
  if [ -f "$target_d/$img_file.000" -o -f "$target_d/$img_file" ]; then
    # The saved image is from partimage
    # get $unzip_stdin_cmd from image file
    if [ -f "$target_d/$img_file.000" ]; then
      get_image_cat_zip_cmd $target_d/$img_file.000
    else
      get_image_cat_zip_cmd $target_d/$img_file
    fi
    start_time="$(date +%s%N)"
    $udpcast_rec_cmd 2>$udpcast_stderr | $unzip_stdin_cmd | partimage \
    $DEFAULT_PARTIMAGE_RESTORE_OPT $PARTIMAGE_RESTORE_OPT restore $part stdin 
    # TODO
    # partimage will return 1 no matter it finishes or not when we use stdin and
    # other options to suppress the warning message... 
    # So just return 0.
    rc=0
    end_time="$(date +%s%N)"
    calculate_elapsed_time $start_time $end_time
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $part, $clone_status, $time_elapsed_in_min mins;"
  elif [ -f "$target_d/$img_file.ntfs-img" ]; then
    # The saved image is from ntfsclone
    ntfs_img_info_tmp="$(mktemp /tmp/ntfs_info.XXXXXX)"
    get_image_cat_zip_cmd $target_d/$img_file.ntfs-img
    start_time="$(date +%s%N)"
    echo "Waiting for multicast clone to start..."
    $udpcast_rec_cmd 2>$udpcast_stderr | $unzip_stdin_cmd | \
    ntfsclone --restore-image --overwrite $part - | tee $ntfs_img_info_tmp
    rc="$?"
    end_time="$(date +%s%N)"
    get_ntfs_image_info $ntfs_img_info_tmp $start_time $end_time
    echo ">>> Time elapsed: $time_elapsed secs (~ $time_elapsed_in_min mins), average speed: $speed"
    # For multicast, with preparation time, it's no so accurate.
    echo ">>> NOTE: The elapsed time may include some preparation time, so the speed is not very accurate."
    [ -f "$ntfs_img_info_tmp" ] && rm -f $ntfs_img_info_tmp
    # prepare statistic report
    conv_return_code_to_human_read $rc
    report_msg="$report_msg $part, $clone_status, $space_used, $time_elapsed_in_min mins, $speed;"
  fi
  echo "Finished multicast restoring image ${target_d##*/} to $part."
  echo $msg_delimiter_star_line
  
  # note! We must sleep a while to let the udp-receiver to finish their jobs then start the next ones.
  echo -n "Preparing the next... "
  # we sleep at least 3 secs.
  sleep 3
  # To avoid all the clients notify at almost same time, we use random sleep before joining multicast.
  # The max time can not be smaller than mcast_max_wait_time
  if [ -n "$mcast_max_wait_time" ]; then
    # -2 secs to make the program have time to run until udpcast request
    time_limit="$(min $SLEEP_TIME_AFTER_PART_CLONED $((mcast_max_wait_time-2)))"
  else
    time_limit="$SLEEP_TIME_AFTER_PART_CLONED"
  fi
  # if < 0, set it as 0
  [ "$time_limit" -le 0 ] && time_limit=0
  [ "$verbose" = "on" ] && echo -n "Max wait time: $time_limit. "
  time="$(get_random_time $time_limit)"
  countdown $time
  echo
  echo $msg_delimiter_star_line
  port="$((port+2))"

  return $rc
} # end of do_multicast_udpcast_restore
#
udp_send_part_img() {
  # part is like: hda1 or lv0l0
  # imagedir, port and ipart are global variable
  local target_d="$1"
  local img_file="$2"
  local part="$3"
  local n
  # for the 2nd and other partition, we should shorten the preparation time
  [ -n "$mcast_wait_time" -a $ipart -gt 1 ] && udpcast_hold_opt1="--min-wait $PART_PREPARE_TIME" 
  [ -n "$mcast_max_wait_time" -a $ipart -gt 1 ] && udpcast_hold_opt3="--max-wait $PART_PREPARE_TIME" 
  udpcast_send_cmd="udp-sender $udp_sender_extra_opt $udpcast_hold_opt1 $udpcast_hold_opt2 $udpcast_hold_opt3 --interface $eth_for_multicast --nokbd --mcast-all-addr $MULTICAST_ALL_ADDR --portbase $port $TIME_TO_LIVE_OPT"
  if [ -f "$imagedir/$target_d/$img_file.000" -o -f "$imagedir/$target_d/$img_file" ]; then
    # The saved image is from partimage
    # get the cat program: cat/zcat/bzcat and zip_stdin_cmd (gzip -c/bzip2 -c/cat)
    if [ -f "$imagedir/$target_d/$img_file.000" ]; then
      get_image_cat_zip_cmd $imagedir/$target_d/$img_file.000
      # The files are split, like hda1.000, hda1.001, so we have to add "."
      img_file_prefix="$img_file.*"
    else
      get_image_cat_zip_cmd $imagedir/$target_d/$img_file
      # The file is NOT split, so the file name is just like "hda1" only, no "."
      img_file_prefix="$img_file"
    fi
    [ -n "$verbose" ] && echo $msg_delimiter_star_line
    [ -n "$verbose" ] && echo "Feeding multicast restoring image $target_d to client's /dev/$part... "
    # The reason we do not use the same skill as do_unicast_stdin_restore
    # is that we want compressed image to be transferred in the network, not
    # the uncompress one, which need a lot of bandwidth.
    n=0
    ( for img in $imagedir/$target_d/$img_file_prefix; do
        n=$((n + 1))
        if [ $n -eq 1 ]; then
          # By separating this, do not compress and uncompress the 1st image.
          # If the VOL_LIMIT is large enough, there is no split images.
          # No uncompress | dd | compress!!!
          cat $img
        else
          $cat_prog $img | dd skip=1 bs=512 2>/dev/null | $zip_stdin_cmd
        fi
      done
    ) | $udpcast_send_cmd 2>$udpcast_stderr
  elif [ -f "$imagedir/$target_d/$img_file.ntfs-img" ]; then
    # we use the original format (do not uncompress) to save the bandwidth
    cat $imagedir/$target_d/$img_file.ntfs-img | $udpcast_send_cmd 2>$udpcast_stderr
  fi
  # prepare parameters for the next 
  ipart="$((ipart+1))"
  port="$((port+2))"
  [ -n "$verbose" ] && echo "done!"
} # end of udp_send_part_img
#
mail_clonezilla_log_to_root() {
  local mail_cli="$1"
  # backup the log file so that it won't be overwritten, since we will run this function in background.
  ocs_job_tmp="$(mktemp /tmp/ocs_job.XXXXXX)"
  cp -f /var/log/clonezilla-jobs.log $ocs_job_tmp
  # mail the clonezilla.log to root.
  $mail_cli -s "Clonezilla report at `date +%F-%R`" root < $ocs_job_tmp
  [ -f "$ocs_job_tmp" ] && rm -f $ocs_job_tmp
}
# function to feed multicast for restoring partitions
feed_multicast_restoreparts() {
  # This is for None-LVM parts
  local tgt_dir="$1"
  local tgt_parts="$2"
  local time_to_pause_before_mail mail_client=""
  # prepare the mail client
  if type mutt &>/dev/null; then
    mail_client="mutt"
  elif type mail &>/dev/null; then
    mail_client="mail"
  elif type mailx &>/dev/null; then
    mail_client="mailx"
  fi
  # Set the initial value for ipart and port.
  ipart=1
  port=$MULTICAST_PORT
  do_LVM_restore_feed="no"
  for partition in $tgt_parts; do
    if [ -n "$(grep -E "^/dev/$partition" $imagedir/$tgt_dir/pt.sf | grep -i "Id=8e")" ]; then
      # do not process LVM partition here, we will process LVM partition and its LV together, later
      do_LVM_restore_feed="yes"
      continue
    fi
    udp_send_part_img $tgt_dir $partition $partition
  done
  # We have to do restore LVM (PV/VG/LV) together, not follow every partition
  if [ "$do_LVM_restore_feed" = "yes" ]; then
    # LVM exists, feed images to restore PV/VG/LV.
    feed_LVM_multicast "$tgt_parts" $port
  fi
  # spawn a background program to wait and mail the clonezilla-jogs.log to root
  (
   # if we can find mail/mutt program then we do it, otherwise skip.
   if [ -n "$mail_client" ]; then
     # When the udp-sender finishes, clonezilla client need to do more:
     # 0. sync the disk
     # 1. prepare the next step (time necessary: $SLEEP_TIME_AFTER_PART_CLONED)
     # 2. restore MBR (time necessary: < 1 sec)
     # 3. run grub-install if assigned (time necessary: maybe 5 secs or more)
     # 4. notify ocsmgrd its job is done (time necessary: $NOTIFY_OCS_SERVER_TIME_LIMIT secs)
     # For 0-3, we estimate it as 30 secs totally.
     time_to_pause_before_mail=$((30+$SLEEP_TIME_AFTER_PART_CLONED+$NOTIFY_OCS_SERVER_TIME_LIMIT))
     sleep $time_to_pause_before_mail
     # Now all clients should finish all their jobs, we can mail now.
     mail_clonezilla_log_to_root $mail_client
   fi
  ) &
  # If always restore, i.e. multicast loop, respawn itself
  if [ "$always_restore" = "yes" ]; then
    mcast_loop="$(( mcast_loop + 1 ))"
    # If it's clonezilla_box_mode, clean dhcpd leases
    if [ "$clonezilla_box_mode" = "yes" ]; then
      echo $msg_delimiter_star_line
      echo "Start the next multicast service (#$mcast_loop). Since it's clonezilla box mode, and always restore, the dhcpd leases are cleaned now...."
      $DRBL_SCRIPT_PATH/sbin/clean-dhcpd-lease &>/dev/null
      echo $msg_delimiter_star_line
    fi
    feed_multicast_restoreparts "$tgt_dir" "$tgt_parts"
  fi
} # end of feed_multicast_restoreparts

# Check if LVM
check_LVM_partition() {
  # This function is to check existing LVM partition
  # part is like "/dev/hda1"
  local part="$1"
  [ -z "$part" ] && return 1
  # Check if the partition is LVM, if so, return 0, else 1
  # Ceasar suggested to use get_part_info:
  # if [ -n "$(file -Ls $part | grep -i "LVM" 2>/dev/null)" ]; then
  if [ -n "$($DRBL_SCRIPT_PATH/sbin/get_part_info $part filesystem | grep -i "LVM        " 2>/dev/null)" ]; then
    return 0
  else 
    return 1
  fi
}

# Save LVM: PV/VG/LV data
# Part of these codes are from http://www.trickytools.com/php/clonesys.php
# Thanks to Jerome Delamarche (jd@inodes-fr.com)
save_logv() {
  local DEV
  local VG
  local UUID
  local LOGV
  local image_name_
  local target="$1"
  PV_PARSE_CONF="$target_dir/lvm_vg_dev.list"
  LOGV_PARSE_CONF="$target_dir/lvm_logv.list"
  # if the report_msg is empty, put the initial one: image_name_
  image_name_="$(basename $target_dir)"
  [ -z "$report_msg" ] && report_msg="Saved $image_name_,"
  rm -f $PV_PARSE_CONF $LOGV_PARSE_CONF
  $DRBL_SCRIPT_PATH/sbin/lvm2-start.sh
  echo "Parsing LVM layout..."
  pvscan | grep lvm2 | while read LINE; do
    DEV=`echo $LINE | tr -s ' ' | cut -d' ' -f2`
    VG=`echo $LINE | tr -s ' ' | cut -d' ' -f4`
    # DEV is like /dev/hda2
    # We just want to keep the chosen target
    # The results in $PV_PARSE_CONF is like:
    # ----------------------------------------------------
    # vg /dev/hda2 qdlt6U-M4bo-xExy-XG9Q-U7pg-bOXN-n54i5Y
    # ----------------------------------------------------
    # Ex: target: hda1 hda2 hda3, DEV maybe: /dev/hda2
    if [ -n "$(echo "$target" | grep -E "\<${DEV##*/}\>")" ]; then
      UUID="$(pvdisplay $DEV | grep "PV UUID" | awk -F" " '{print $3}')"
      echo "$VG $DEV $UUID" | tee -a $PV_PARSE_CONF
    fi
  done
  
  # We just want the LV in chosen target
  echo "Parsing logical volumes..."
  # lvscan results are like:
  # ACTIVE            '/dev/vg3/lvol0' [1.20 GB] inherit
  # ACTIVE            '/dev/vg3/lvol1' [648.00 MB] inherit
  # ACTIVE            '/dev/vg/lvol0' [1.50 GB] inherit
  # ACTIVE            '/dev/vg/lvol1' [500.00 MB] inherit
  #                         ^^ The VG
  lvscan | grep "/.*/" | while read LINE; do
    LOGV=`echo $LINE |tr -s ' ' | cut -d' ' -f2 | tr -d "'"`
    # LOGV is like: /dev/vg3/lvol0
    while read vg dev uuid; do
     # only keep LOGV is in the chosen vg
     if [ -n "$(echo $LOGV | grep -E "/dev/$vg/" 2>/dev/null)" ]; then
      file_system="$(file -Ls $LOGV | awk -F":" '{print $2}')"
      echo "$LOGV $file_system" | tee -a $LOGV_PARSE_CONF
     fi 
    done < $PV_PARSE_CONF
  done
  
  # Backup the vg conf
  echo "Saving the VG config... "
  while read vg dev uuid; do
    vgcfgbackup -f $target_dir/lvm_$vg.conf $vg 2>/dev/null
  done < $PV_PARSE_CONF
  echo "done!"
  [ ! -f "$PV_PARSE_CONF" ] && exit 1
  [ ! -f "$LOGV_PARSE_CONF" ] && exit 1
  while read lv fs; do
   [ ! -e "$lv" ] && continue
   fn="$(echo $lv | sed -e "s|^/dev/||" -e "s|/|-|g")"
   # skip swap or extended partition
   case "$fs" in *[Ss][Ww][Aa][Pp]*|*extended*) continue ;; esac
   echo "Saving device $fn..."
   start_time="$(date +%s%N)"
   partimage -M -f3 -o -d -b -c -z1 save $lv $target_dir/$fn
   end_time="$(date +%s%N)"
   calculate_elapsed_time $start_time $end_time
   # prepare statistic report
   conv_return_code_to_human_read $rc
   report_msg="$report_msg $lv, $clone_status, $time_elapsed_in_min mins;"
   echo $msg_delimiter_star_line
   echo "Finished saving $lv as $fn"
  done < $LOGV_PARSE_CONF
} # end of save_logv

# Restore LVM: PV/VG/LV data
# Part of these codes are from http://www.trickytools.com/php/clonesys.php
# Thanks to Jerome Delamarche (jd@inodes-fr.com)
restore_logv() {
  # mode is unicast or multicast
  local tgt_parts="$1"
  local mode="$2"
  local port="$3"
  local volg
  if [ -z "$mode" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "To restore LVM, you must specify it's unicast or multicast!!!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "Program terminated!!!"
    exit 1
  fi
  PV_PARSE_CONF="$target_dir/lvm_vg_dev.list"
  LOGV_PARSE_CONF="$target_dir/lvm_logv.list"
  [ ! -f "$PV_PARSE_CONF" ] && exit 1
  [ ! -f "$LOGV_PARSE_CONF" ] && exit 1
  #
  $DRBL_SCRIPT_PATH/sbin/lvm2-stop.sh
  # create PV first
  echo "Creating the PV... "
  while read vg dev uuid; do
    pvcreate -ff --yes --uuid $uuid $dev
  done < $PV_PARSE_CONF
  echo "done!"
  
  # Restore the vg conf
  echo "Restoring the VG config... "
  while read vg dev uuid; do
    vgcfgrestore -f $target_dir/lvm_$vg.conf $vg 2>/dev/null
  done < $PV_PARSE_CONF
  echo "done!"
  #
  $DRBL_SCRIPT_PATH/sbin/lvm2-start.sh
  
  while read lv fs; do
   [ ! -e $lv ] && continue
   # Then we process the real data partition, only those in the chosen partitions
   # Ex:
   # /dev/vg3/lvol0  Linux rev 1.0 ext3 filesystem data (large files)
   # Then lvol0 is belong to VG vg3
   volg="$(echo "$lv" | awk -F"/" '{print $3}')"
   # Find if the LV is in the chosen partition (via VG, we can determine that)
   # EX: tgt_parts: hda1, hda3, hda5...
   #     vg3 /dev/hda3 nPMQQ0-D2yN-YRHL-9fBM-0cUm-vgcw-DCUTri
   is_in_chosen_partition="no"
   for ipt in $tgt_parts; do
     if [ -n "$(grep -E "[[:space:]]+/dev/$ipt[[:space:]]+" $PV_PARSE_CONF | grep -E "\<$volg\>")" ]; then
       # Found the chosen partitions is in the VG
       is_in_chosen_partition="yes"
       break
     fi
   done
   # If not in the chosen partition, skip this, continue with the next.
   [ "$is_in_chosen_partition" = "no" ] && continue
   # create the swap if it's swap partition
   case "$fs" in 
     *[Ss][Ww][Aa][Pp]*)
        echo "Found the swap partition $lv, creat it:"
        mkswap $lv
        echo $msg_delimiter_star_line
	# then skip the rest.
        continue;; 
   esac
   fn="$(echo $lv | sed -e "s|^/dev/||" -e "s|/|-|g")"
   echo "Saving device $fn..."
   case "$mode" in
     unicast)
       do_unicast_stdin_restore $target_dir $fn $lv
       ;;
     multicast)
       do_multicast_udpcast_restore $target_dir $fn $lv
       ;;
   esac
  done < $LOGV_PARSE_CONF
} # end of restore_logv

# Multicast feeding to Restore LVM: PV/VG/LV data
# Part of these codes are from http://www.trickytools.com/php/clonesys.php
# Thanks to Jerome Delamarche (jd@inodes-fr.com)
feed_LVM_multicast() {
  # This is for LVM parts
  # imagedir, port and ipart are global variable
  local tgt_parts="$1"
  local port="$2"
  local volg n
  if [ -z "$port" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "To feed multicast LVM, you must specify the port!!!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "Program terminated!!!"
    exit 1
  fi
  # Note! With a leading $imagedir before /$target_dir
  PV_PARSE_CONF="$imagedir/$target_dir/lvm_vg_dev.list"
  LOGV_PARSE_CONF="$imagedir/$target_dir/lvm_logv.list"
  [ ! -f "$PV_PARSE_CONF" ] && exit 1
  [ ! -f "$LOGV_PARSE_CONF" ] && exit 1
  
  while read lv fs; do
   # Process the real data partition, only those in the chosen partitions
   # Ex:
   # /dev/vg3/lvol0  Linux rev 1.0 ext3 filesystem data (large files)
   # Then lvol0 is belong to VG vg3
   volg="$(echo "$lv" | awk -F"/" '{print $3}')"
   # Find if the LV is in the chosen partition (via VG, we can determine that)
   # EX: tgt_parts: hda1, hda3, hda5...
   #     vg3 /dev/hda3 nPMQQ0-D2yN-YRHL-9fBM-0cUm-vgcw-DCUTri
   is_in_chosen_partition="no"
   for ipt in $tgt_parts; do
     if [ -n "$(grep -E "[[:space:]]+/dev/$ipt[[:space:]]+" $PV_PARSE_CONF | grep -E "\<$volg\>")" ]; then
       # Found the chosen partitions is in the VG
       is_in_chosen_partition="yes"
       break
     fi
   done
   [ "$is_in_chosen_partition" = "no" ] && continue
   fn="$(echo $lv | sed -e "s|^/dev/||" -e "s|/|-|g")"
   [ ! -e "$imagedir/$target_dir/$fn.000" -a \
     ! -e "$imagedir/$target_dir/$fn" ] && continue
   udp_send_part_img $target_dir $fn $lv
  done < $LOGV_PARSE_CONF
}
#
check_target_hd() {
  local disk_file="$1"
  local tgt_hd="$2"
  if [ -z "$tgt_hd" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "The target harddisk you assigned is nothing!"
    echo "$disk_file is empty ?"
    echo "Maybe diskfull when you saved image ?"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "Program terminated!"
    exit 1
  fi
}
#
check_target_parts() {
  local parts_file="$1"
  local tgt_parts="$2"
  if [ -z "$tgt_parts" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "The target partitions you assigned is nothing!"
    echo "$parts_file is empty ?"
    echo "Maybe diskfull when you saved image ?"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "Program terminated!"
    exit 1
  fi
}
