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

###
### functions for drbl-ocs and ocs-onthefly
###
#
partimage_save() {
    local source_dev="$1"
    local target_part_file="$2"
    echo $msg_delimiter_star_line
    echo "Starting saving $source_dev as ${target_part_file}.XXX..."
    partimage $DEFAULT_PARTIMAGE_SAVE_OPT $PARTIMAGE_SAVE_OPT save $source_dev $target_part_file
    echo "Finished saving $source_dev as ${target_part_file}.XXX"
    echo $msg_delimiter_star_line
    rc="$?"
    if [ $rc -gt 0 ]; then
       [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
       echo "Failed to save partition $source_dev! $msg_press_enter_to_continue..."
       [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
       read
    fi
}

# 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"
    echo $msg_delimiter_star_line
    # 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 does not exist, so 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() {
  # sfdisk_target_hd: /dev/hda, /dev/hdb...
  # sfdisk_partition_layout_file can be get from "sfdisk -d /dev/hda"
  local sfdisk_target_hd="$1"
  local sfdisk_partition_layout_file="$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 [ "$run_sfdisk" = "yes" ]; then
    # Before create partition, clean the warning message in pt.sf so that sfdisk
    # will not refuse to do that.
    date | tee $RESTORE_SFDISK_LOG
    echo "Writinging the partition table..." | tee -a $RESTORE_SFDISK_LOG
    echo "Running sfdisk $sf_opt $sfdisk_target_hd < $sfdisk_partition_layout_file" | tee -a $RESTORE_SFDISK_LOG
    sfdisk $sf_opt $sfdisk_target_hd < $sfdisk_partition_layout_file | tee -a $RESTORE_SFDISK_LOG
    RETVAL="$?"
    echo "RETVAL=$RETVAL"
    echo "This is done by sfdisk $sf_opt $sfdisk_target_hd < $sfdisk_partition_layout_file" | 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
  fi
}

#
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`
   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="$2"
    TMP=`mktemp /tmp/ocs.XXXXXX`
    trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
    ASK_IMGNAME=1
    while [ "$ASK_IMGNAME" -ne 0 ]; do
      $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
         --inputbox "$msg_input_name_to_save_the_img $tgt_name" 0 0 "" 2> $TMP
      tgt_img_name=$(cat $TMP)
      if [ -z "$tgt_img_name" ]; then
        $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
        --msgbox "$msg_you_must_input_filename!\n\n$msg_please_do_it_again!!!" 0 0 
      elif [ -n "$(ls $ocsroot/$tgt_img_name 2>/dev/null)" ]; 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
}

#
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 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

            # the parted output example:
            # Disk geometry for /dev/sda5: 0.000-735.984 megabytes
            # Disk label type: loop
            # Minor    Start       End     Filesystem  Flags
            # 1          0.000    735.984  reiserfs
            DEV_MODEL="$(parted -s /dev/${p:0:3} p | awk -F" " "/^${p:3}\>/ {print \$5}")"
            ;;
          *) ;;
        esac
        [ -z "$DEV_MODEL" ] && DEV_MODEL="No_description"
        HARDDEVS="$HARDDEVS $p $DEV_MODEL off"
    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
}

#
get_existing_hda1_image() {
   # 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 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
     $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)
     [ -f "$TMP" ] && rm -f $TMP
     [ -z "$tgt_file" ] && echo "No filename selected! Program terminated!!!" && exit 1
   else
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "No Image in $imagedir.. Please make sure you already saved an image! (abort!!!)"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     exit 1
   fi
   [ -f "$TMP" ] && rm -f $TMP
   # return the valule
   echo $tgt_file > $ANS_TMP
}

#
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_parts=
   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
     $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_parts=$(cat $TMP)
   else
     echo "No Image in $imagedir.. (abort)"
     exit 1
   fi
   [ -f "$TMP" ] && rm -f $TMP
   # return the valule
   echo $tgt_parts > $ANS_TMP
}

#
get_existing_partitions () {
   local ANS_TMP=$1
   local tgt_file=$2
   local tgt_part
   # if the prvious $tgt_file is created by savedisk, we will generate parts now.
   if [ ! -f "$imagedir/$tgt_file/parts" -a -f "$imagedir/$tgt_file/pt.sf" ] ; then
     for part in `get_known_partition $imagedir/$tgt_file/pt.sf` ; do
       echo -n "$part " >> $imagedir/$tgt_file/parts
     done
   fi
   #
   NUMPARTS="$(wc -w $imagedir/$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 $imagedir/$tgt_file/parts` ; do echo "$p" "disk("${p:0:3}")_partition("${p:3}")" off ; done)"
     $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)
     [ -f "$TMP" ] && rm -f $TMP
   else
     echo "Unable to find any partition image file in $tgt_file! Program terminated!!!"
   fi
   # return the valule
   echo $tgt_parts > $ANS_TMP
}

#
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() {
    for eth in `get_dhcpd_interface`; do
      # keep the port for multicast to be echoed in screen
      eth_for_multicast="$eth"
      echo -n "Using ethernet port $eth_for_multicast..."
      break
    done
    echo "done."
    # echo notes for network environment in multicast clonezilla
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$msg_you_are_using_multicast_clonezilla"
    echo "$msg_ethernet_port_is_up_confirm: $eth_for_multicast "
    echo "$msg_more_NIC_connect_each_other"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    if [ "$batch_mode" != "on" ]; then
      echo -n "$msg_press_enter_to_continue..."
      read
    fi
}
# function to feed multicast for restoring partitions
feed_multicast_restoreparts() {
  # This is for None-LVM parts
  local option="$1"
  local target_parts="$2"
  local ipart=1
  port=$MULTICAST_PORT
  # the target partition maybe like ""hda3"", we need to strip the "
  target_dir="$(echo "$option" | sed -e "s/\"//g" | awk -F" " '{print $1}')"
  if [ -z "$target_parts" ]; then
    target_parts="$(echo "$option" | sed -e "s/\"//g" | awk -F" " "{for (i = 2; i <= NF; i = i + 1) print \$i}")"
  fi
  do_LVM_restore_feed="no"
  for partition in $target_parts; do
    if [ -n "$(grep -E "^/dev/$partition" $imagedir/$target_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
    # get the cat program: cat/zcat/bzcat and zip_cmd (gzip -c/bzip2 -c/cat)
    get_image_cat_zip_cmd $imagedir/$target_dir/$partition.000
    echo $msg_delimiter_star_line
    echo "Starting multicast restoring image $target_dir to /dev/$partition..."
    # 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" 
    # The reasone 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_dir/$partition.*; 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 enought, so there is no split images.
          #No uncompress | dd | compress!!!
          cat $img
        else
          $cat_prog $img | dd skip=1 bs=512 2>/dev/null | $zip_cmd
        fi
      done
    ) | udp-sender $udpcast_hold_opt1 $udpcast_hold_opt2 $udpcast_hold_opt3 --interface $eth_for_multicast --nokbd --mcast-all-addr $MULTICAST_ADDR --portbase $port $TIME_TO_LIVE_OPT 2>/dev/null &
    ipart=`expr $ipart + 1`
    port=`expr $port + 2`
  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 "$target_parts" $port
  fi
} # end of feed_multicast_restoreparts

# Check if LVM
check_LVM_partition() {
  # This function is to check existing LVM partiton
  # part is like "/dev/hda1"
  local part="$1"
  [ -z "$part" ] && return 1
  # Check if the partition is LVM, if so, return 0, else 1
  if [ -n "$(file -Ls $part | 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 target="$1"
  PV_PARSE_CONF="$target_dir/lvm_vg_dev.list"
  LOGV_PARSE_CONF="$target_dir/lvm_logv.list"
  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..."
   partimage -M -f3 -o -d -b -c -z1 save $lv $target_dir/$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 partiton, 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)
       #partimage -M -f3 -o -d -b -c restore $lv $target_dir/$fn.000
       do_unicast_stdin_restore $target_dir $fn $lv
       ;;
     multicast)
       udp-receiver --nokbd --mcast-all-addr $MULTICAST_ADDR --portbase $port $TIME_TO_LIVE_OPT 2> /dev/null | $unzip_cmd | partimage $DEFAULT_PARTIMAGE_RESTORE_OPT $PARTIMAGE_RESTORE_OPT restore $lv stdin 
       echo "Finished multicast restoring image ${target_dir##*/} to $lv."
       echo $msg_delimiter_star_line
       # note! We must sleep a while to let the udp-receivers to finish their jobs then start the next ones.
       echo -n "Preparing the next... "
       countdown $SLEEP_TIME_AFTER_PART_CLONED
       echo
       port=`expr $port + 2`
       ;;
   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
  local tgt_parts="$1"
  local port="$2"
  local volg
  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" ] && continue
   get_image_cat_zip_cmd $imagedir/$target_dir/$fn.000
   echo $msg_delimiter_star_line
   echo "Starting multicast restoring image $target_dir to /dev/$lv..."
   # 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" 
   # 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_dir/$fn.*; 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 enought, so there is no split images.
         #No uncompress | dd | compress!!!
         cat $img
       else
         $cat_prog $img | dd skip=1 bs=512 2>/dev/null | $zip_cmd
       fi
     done
   ) | udp-sender $udpcast_hold_opt1 $udpcast_hold_opt2 $udpcast_hold_opt3 --interface $eth_for_multicast --nokbd --mcast-all-addr $MULTICAST_ADDR --portbase $port $TIME_TO_LIVE_OPT 2>/dev/null &
   ipart=`expr $ipart + 1`
   port=`expr $port + 2`
  done < $LOGV_PARSE_CONF
}
