#!/bin/bash
# License: GPL 
# Author: Steven Shiau <steven _at_ clonezilla org>
# Description: Program to feed multicast/bt packets for clients to restore.
# What will be done in this program:
# 1. Image repository has to be mounted first.
# 2. Create a tarball about the image which contains all files in the image except file system image files.
# 3. Provide full command for client to run. 
# For 2 and 3, the files will be put in httpd server.
# //NOTE// Bittorrent is still under development. Not enabled yet.

#
DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
. /etc/drbl/drbl-ocs.conf
. $DRBL_SCRIPT_PATH/sbin/ocs-functions

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

# Settings
# OCS_OPT is for ocs-sr
OCS_OPT=
nfs_restart="no"
udpcast_stderr="/dev/null"
udp_sender_extra_opt="$udp_sender_extra_opt_default"
run_prerun_dir="no"
run_postrun_dir="no"
create_part="yes"
restore_mbr="yes"
restore_prebuild_mbr="no"
rm_win_swap_hib="no"
chk_img_restoreable_on_srv_def="yes"
# Timeout (secs) for wating udp-sender
timeout_max="300"
# Network settings for closed LAN
ipadd_closed_lan="192.168.169.250"
netmask_closed_lan="24"
gwadd_closed_lan="192.168.169.254"
dhcp_rang_start_closed_lan="192.168.169.101"
dhcp_rang_end_closed_lan="192.168.169.200"

# Functions
USAGE() {
    echo "$ocs - To start feeding image for multicast/bt mode in Clonezilla"
    echo "Usage:"
    echo "To run $ocs:"
    echo "$ocs [OPTION] MODE [IMAGE_NAME] [DEVICE]"
    echo "This program is specially used in Clonezilla live to start feeding multicast/bt packets for clients."
    echo "Options:"
    USAGE_common_restore
    USAGE_common_restore_server
    USAGE_reserved_word_for_restore
    echo
    echo " General options:"
    USAGE_common_general
    dialog_like_prog_help_prompt
    echo " -s, --skip-hw-detect     Skip the hardware detection (kudzu, harddrake or discover)"
    echo " -n, --no-nfs-restart     Do not to restart nfs when start or stop $ocs (This is default)"
    echo " -f, --nfs-restart        Restart nfs when start or stop $ocs (Default is to restart nfs)"
    echo " --mcast-iface  PORT      Specify the multicast seed ethernet port PORT (eth0, eth1, eth2...). Suppose clonezilla will try to find that for you, but in some case, you might want to specify that."
    echo " -x, --full-duplex         Use full-duplex network with udpcast in multicast mode (faster, but does not work with network hub, only works in switch.)."
    echo " -md, --massive-deploy-mode Assign the mass deployment mode: broadcast, multicast, or bittorrent"
    echo
    echo "MODE is \"start\" or \"stop\""
    echo "IMAGE_NAME is the image dir name, not absolute path"
    echo "DEVICE is the device name, e.g. sda, sda1, sda2..."
    echo "If \"ask_user\" is used as IMAGE_NAME or DEVICE, a dialog menu will be shown to allow selection."
    echo "If no IMAGE_NAME is specified, a dialog menu will be shown."
    echo "Ex:"
    echo "To start feeding the image \"my-image\" for restoring it in client's device sda, run"
    echo "   $ocs start my-image sda"
    echo
} # end of USAGE
#
parse_ocs_live_feed_img_cmd_options_with_dash() { 
  n_shift=0
  #
  while [ $# -gt 0 ]; do
   case "$1" in
      -p|--postaction)  
	    shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              postaction="$1"
	      shift; n_shift=$((n_shift+1))
            fi
            OCS_OPT="$OCS_OPT -p $postaction"
            ;;
      -g|--grub-install)  
            install_grub="on"
  	    shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              grub_partition=$1
  	    shift; n_shift=$((n_shift+1))
            fi
            [ -z "$grub_partition" ] && USAGE && exit 1
  	    OCS_OPT="$OCS_OPT -g $grub_partition"
            ;;
      -a|--no-force-dma-on)  
  	    OCS_OPT="$OCS_OPT --no-force-dma-on"
  	    shift; n_shift=$((n_shift+1));;
      -k|--no-fdisk|--no-create-partition)  
  	    create_part="no"
  	    OCS_OPT="$OCS_OPT -k"
  	    shift; n_shift=$((n_shift+1));;
      -k1|--fdisk-proportion)  
  	    create_part="yes"
  	    create_part_type="proportion"
  	    OCS_OPT="$OCS_OPT -k1"
  	    shift; n_shift=$((n_shift+1));;
      -k2|--fdisk-manual)  
  	    create_part="yes"
  	    create_part_type="manual"
  	    OCS_OPT="$OCS_OPT -k2"
  	    shift; n_shift=$((n_shift+1));;
      -t|--no-restore-mbr)  
  	    restore_mbr="no"
  	    OCS_OPT="$OCS_OPT -t"
              shift; n_shift=$((n_shift+1));;
      -t1|--restore-raw-mbr)  
              # The flag to restore syslinux mbr.bin to M$ windows system.
  	    restore_prebuild_mbr="yes"
  	    OCS_OPT="$OCS_OPT -t1"
              shift; n_shift=$((n_shift+1));;
      -t2|--no-restore-ebr)  
  	    restore_ebr="no"
  	    OCS_OPT="$OCS_OPT -t2"
              shift; n_shift=$((n_shift+1));;
      -e|--load-geometry)
  	    OCS_OPT="$OCS_OPT -e"
              shift; n_shift=$((n_shift+1));;
      -e1|--change-geometry)  
              change_ntfs_boot_chs="on"
              shift; n_shift=$((n_shift+1))
              # skip the -xx option, in case 
              if [ -z "$(echo $1 |grep ^-.)" ]; then
                ntfs_boot_partition=$1
                shift; n_shift=$((n_shift+1))
              fi
              [ -z "$ntfs_boot_partition" ] && USAGE && exit 1
  	    OCS_OPT="$OCS_OPT -e1 $ntfs_boot_partition"
              ;;
      -e2|--load-geometry-from-edd)
  	    OCS_OPT="$OCS_OPT -e2"
              shift; n_shift=$((n_shift+1));;
      -c|--confirm)
  	    OCS_OPT="$OCS_OPT -c"
  	    shift; n_shift=$((n_shift+1));;
      -w|--wait-time)
              shift; n_shift=$((n_shift+1))
              if [ -z "$(echo $1 |grep ^-.)" ]; then
                # skip the -xx option, in case 
  	      TIME_to_wait="$1"
  	      OCS_OPT="$OCS_OPT -w $TIME_to_wait"
                shift; n_shift=$((n_shift+1))
              fi
  	    ;;
      -s|--skip-hw-detect)
              hw_detect="off"
  	    shift; n_shift=$((n_shift+1));;
      --debug=?*)
            debug_level=${1#--debug=}
  	    OCS_OPT="$OCS_OPT --debug=$debug_level"
  	    shift; n_shift=$((n_shift+1));;
      -b|--batch)
            # Here we append --batch to OCS_OPT instead of -b, (it will confuse init, sinice -b for init is emergency mode)
            ocs_batch_mode="on"
  	    OCS_OPT="$OCS_OPT --batch"
  	    shift; n_shift=$((n_shift+1));;
      -d|--debug-mode)
            debug_mode="on"
  	    OCS_OPT="$OCS_OPT -d"
  	    shift; n_shift=$((n_shift+1));;
      -d0|--dialog)   DIA="dialog"; shift; n_shift=$((n_shift+1));;
      -d1|--Xdialog)  DIA="Xdialog"; shift; n_shift=$((n_shift+1));;
      -d2|--whiptail) DIA="whiptail"; shift; n_shift=$((n_shift+1));;
      -d3|--gdialog)  DIA="gdialog"; shift; n_shift=$((n_shift+1));;
      -d4|--kdialog)  DIA="kdialog"; shift; n_shift=$((n_shift+1));;
      -r|--resize-partition)
  	    OCS_OPT="$OCS_OPT -r"
  	    shift; n_shift=$((n_shift+1));;
      -v|--verbose)
  	    verbose="on"
  	    OCS_OPT="$OCS_OPT -v"
  	    shift; n_shift=$((n_shift+1));;
      -nogui|--nogui)
            # -nogui is for backward compatable, better to use --nogui
            OCS_OPT="$OCS_OPT --nogui"
  	    shift; n_shift=$((n_shift+1));;
      -rm-win-swap-hib|--rm-win-swap-hib)
            # Remove page and hibernation files in M$ windows
            OCS_OPT="$OCS_OPT --rm-win-swap-hib"
  	    shift; n_shift=$((n_shift+1));;
      -rescue|--rescue)
            OCS_OPT="$OCS_OPT --rescue"
  	    shift; n_shift=$((n_shift+1));;
      -mp|--mount-point)
            shift; 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              mount_point=$1
  	    shift; n_shift=$((n_shift+1))
            fi
            [ -z "$mount_point" ] && USAGE && exit 1
  	    OCS_OPT="$OCS_OPT --mount-point $mount_point" ; 
  	    ;;
      --time-to-wait)
  	    shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
  	    mcast_wait_time="$1"
  	    shift; n_shift=$((n_shift+1))
            fi
  	    ;;
      --clients-to-wait)
  	    shift; n_shift=$((n_shift+1))
              if [ -z "$(echo $1 |grep ^-.)" ]; then
                # skip the -xx option, in case 
  	      n_clients="$1"
  	      shift; n_shift=$((n_shift+1))
              fi
  	    ;;
      --max-time-to-wait)
  	    shift; n_shift=$((n_shift+1))
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
  	    mcast_max_wait_time="$1"
  	    shift; n_shift=$((n_shift+1))
            fi
            [ -z "$mcast_max_wait_time" ] && USAGE && exit 1
  	    # Client need to know the --max-time-to-wait so that when sleeping
  	    # between partitions restoring clone won't timeout.
            OCS_OPT="$OCS_OPT --max-time-to-wait $mcast_max_wait_time"
  	    ;;
      -x|--full-duplex)
            udp_sender_extra_opt="$udp_sender_extra_opt --full-duplex"
  	    shift; n_shift=$((n_shift+1));;
      -brdcst|--broadcast)
            udp_sender_extra_opt="$udp_sender_extra_opt --broadcast"
  	    shift; n_shift=$((n_shift+1));;
      -j|--create-part-by-sfdisk)
            # We leave this option for backward compatability.
  	    OCS_OPT="$OCS_OPT --create-part-by-sfdisk"
  	    shift; n_shift=$((n_shift+1));;
      -j0|--create-part-by-dd)
  	    OCS_OPT="$OCS_OPT --create-part-by-dd"
  	    shift; n_shift=$((n_shift+1));;
      -j1|--dump-mbr-in-the-end)
  	    OCS_OPT="$OCS_OPT --dump-mbr-in-the-end"
  	    shift; n_shift=$((n_shift+1));;
      -j2|--clone-hidden-data)
  	    OCS_OPT="$OCS_OPT --clone-hidden-data"
  	    shift; n_shift=$((n_shift+1));;
      -icrc|--icrc)
  	    OCS_OPT="$OCS_OPT --icrc"
  	    shift; n_shift=$((n_shift+1));;
      -irvd|--irvd)
  	    OCS_OPT="$OCS_OPT --irvd"
  	    shift; n_shift=$((n_shift+1));;
      -irhr|--irhr)
  	    OCS_OPT="$OCS_OPT --irhr"
  	    shift; n_shift=$((n_shift+1));;
      -ius|--ius)
  	    OCS_OPT="$OCS_OPT --ius"
  	    shift; n_shift=$((n_shift+1));;
      -icds|--ignore-chk-dsk-size-pt)
  	    OCS_OPT="$OCS_OPT --ignore-chk-dsk-size-pt"
  	    shift; n_shift=$((n_shift+1));;
      -iefi|--ignore-update-efi-nvram)
  	    OCS_OPT="$OCS_OPT --ignore-update-efi-nvram"
  	    shift; n_shift=$((n_shift+1));;
      -srel|--save-restore-error-log)
  	    OCS_OPT="$OCS_OPT --save-restore-error-log"
  	    shift; n_shift=$((n_shift+1));;
      -hn0)
  	    shift; n_shift=$((n_shift+1))
            change_win_hostname="By_IP"
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
  	    win_hostname_prefix="$1"
  	    shift; n_shift=$((n_shift+1))
            fi
            [ -z "$win_hostname_prefix" ] && USAGE && exit 1
            OCS_OPT="$OCS_OPT -hn0 $win_hostname_prefix"
  	    ;;
      -hn1)
  	    shift; n_shift=$((n_shift+1))
            change_win_hostname="By_MAC"
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
  	    win_hostname_prefix="$1"
  	    shift; n_shift=$((n_shift+1))
            fi
            [ -z "$win_hostname_prefix" ] && USAGE && exit 1
            OCS_OPT="$OCS_OPT -hn1 $win_hostname_prefix"
  	    ;;
      -o|-o1|--run-postrun-dir) 
              # -o is for backward compatability
  	    run_postrun_dir="yes"
  	    OCS_OPT="$OCS_OPT --run-postrun-dir"
  	    shift; n_shift=$((n_shift+1));;
      -o0|--run-prerun-dir) 
  	    run_prerun_dir="yes"
  	    OCS_OPT="$OCS_OPT --run-prerun-dir"
  	    shift; n_shift=$((n_shift+1));;
      -ns|--ntfs-progress-in-image-dir)
  	    OCS_OPT="$OCS_OPT --ntfs-progress-in-image-dir"
  	    shift; n_shift=$((n_shift+1));;
      -cm|--check-md5sum)
  	    check_md5sum="yes"
  	    OCS_OPT="$OCS_OPT -cm"
  	    shift; n_shift=$((n_shift+1));;
      -cs|--check-sha1sum)
  	    check_sha1sum="yes"
  	    OCS_OPT="$OCS_OPT -cs"
  	    shift; n_shift=$((n_shift+1));;
      -cmf|--chk-chksum-for-files-in-dev)
  	    chk_chksum_for_files_in_dev="yes"
  	    OCS_OPT="$OCS_OPT -cmf"
  	    shift; n_shift=$((n_shift+1));;
      --mcast-iface)
  	    shift; n_shift=$((n_shift+1))
              if [ -z "$(echo $1 |grep ^-.)" ]; then
                # skip the -xx option, in case 
  	      eth_for_multicast="$1"
  	      shift; n_shift=$((n_shift+1))
              fi
              [ -z "$eth_for_multicast" ] && USAGE && exit 1
  	    ;;
      -um|--user-mode)
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              ocs_user_mode="$1"
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$ocs_user_mode" ] && USAGE && exit 1
	    # There is no need to pass user mode because it's already in batch mode.
  	    # XXXXXX OCS_OPT="$OCS_OPT -um $ocs_user_mode"
  	    ;;
      -sc0|--skip-check-restorable-on-srv)
            # flag to check if the image is restorable on clonezilla server
  	    chk_img_restoreable_on_srv="no"
  	    shift; n_shift=$((n_shift+1));;
      -sc|-scs|--skip-check-restorable|--skip-check-restorable-s)
            # flag to check if the image is restorable
  	    chk_img_restoreable_mod_save="no"
  	    OCS_OPT="$OCS_OPT -scs"
  	    shift; n_shift=$((n_shift+1));;
      -scr|--skip-check-restorable-r)
            # Flag to check if the image is restorable
  	    chk_img_restoreable_mod_restore="no"
  	    OCS_OPT="$OCS_OPT -scr"
  	    shift; n_shift=$((n_shift+1));;
      -md|--massive-deploy-mode)
            shift; n_shift=$((n_shift+1))
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              massive_deploy_mode="$1"
              shift; n_shift=$((n_shift+1))
            fi
            [ -z "$massive_deploy_mode" ] && USAGE && exit 1
	    # //NOTE// Do not pass -md option to OCS_OPT here.
	    # OCS_OPT is for ocs-sr, not for ocs-live-feed-img
  	    # XXXXXX OCS_OPT="$OCS_OPT -md $massive_deploy_mode"
  	    ;;
      -*)   echo "${0}: ${1}: invalid option" >&2
            USAGE >& 2
            exit 2 ;;
       *)   break ;;
   esac
  done
} # end of parse_ocs_live_feed_img_cmd_options_with_dash
#
config_netcard() {
  local status_done
  # nic_dev_only is global variable
  # Get the NIC device name for the only one device
  # TODO: find a better way to find when there are multiple NICs?
  nic_dev_only="$(get-nic-devs | head -n 1)"
  ip address add $ipadd_closed_lan/$netmask_closed_lan dev ${nic_dev_only}
  ip link set ${nic_dev_only} up
  # Set the routing
  ip route add default via $gwadd_closed_lan dev ${nic_dev_only}
  echo "$msg_delimiter_star_line"
  echo "Network settings for ${nic_dev_only}:"
  ip address show ${nic_dev_only}
} # end of config_netcard
#
gen_lite_dhcpd_conf() {
  local srv_cfg="$1"
  if [ -e "$dnsmasq_cfg" ]; then
    mv -f $dnsmasq_cfg ${dnsmasq_cfg}.orig
  fi
  cat <<-DNSMQSQ_END >> $dnsmasq_cfg 
# Configured by Clonezilla live
dhcp-range=${dhcp_rang_start_closed_lan},${dhcp_rang_end_closed_lan}
DNSMQSQ_END
  echo "$msg_delimiter_star_line"
  echo "DHCP service settings in $dnsmasq_cfg:"
  cat $dnsmasq_cfg
  echo "$msg_delimiter_star_line"
} # end of gen_lite_dhcpd_conf
#
start_nat_service() {
  local subnet
  NAT_RULES_FOR_DEBIAN=/etc/default/ocs-live-lite-nat.rules
  subnet="$(LC_ALL=C drbl-ipcalc $ipadd_closed_lan/$netmask_closed_lan | awk -F' ' '/Network:/ {print $2}')"
  # Flush all rules first
  iptables -F
  iptables -t nat -F
  iptables -t mangle -F
  # Configure it 
  iptables -t nat -A POSTROUTING -s ${subnet} -j MASQUERADE
  iptables-save > $NAT_RULES_FOR_DEBIAN
  # Enable IP forwarding
  echo 1 > /proc/sys/net/ipv4/ip_forward
} # end of start_nat_service
#
task_start_feed_img_for_mcast() {
  local MCAST_TMP ans_ mcast_dev_action
  local start_cmd
  local OCS_OPT_PRE
  local interactive_mode="no"
  local rc_chkimg
  local castmode

  [ -z "$ocs_restore_img_name" ] && ocs_restore_img_name="ask_user"
  [ -z "$ocs_restore_dev" ] && ocs_restore_dev="ask_user"

  # If network setting is not configured. Ask
  configured_ip="$(get-all-nic-ip --all-ip-address)"
  if [ -z "$configured_ip" ]; then
    if [ -z "$netenv_mode" ]; then
      TMP="$(mktemp /tmp/ocslivemode.XXXXXX)"
      trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
      $DIA --backtitle "$msg_nchc_free_software_labs" --title  \
      "Network environment" --menu "$msg_which_network_senario\n$msg_choose_mode:" \
      0 0 0 $DIA_ESC \
      "To_upstream"  "$msg_connect_to_upstream_network" \
      "In_isolation" "$msg_in_isolated_environment" \
      2> $TMP
      netenv_mode="$(cat $TMP)"
      echo "netenv_mode is $netenv_mode"
      [ -f "$TMP" ] && rm -f $TMP
      
    fi
    case "$netenv_mode" in
      "To_upstream") 
       # Even if it's local repository, we still have to configure network
       # otherwise the udp-sender won't start.
       network_config_if_necessary
         ;;
      "In_isolation")
         echo "In isolation environment. Now start dhcp service..." 
         config_netcard
         gen_lite_dhcpd_conf $dnsmasq_cfg
         service dnsmasq restart
         start_nat_service
         echo -n "$msg_press_enter_to_continue"
         read 
         ;;
      *) echo "$msg_program_stop" | tee --append ${OCS_LOGFILE}
         exit 0
         ;;
    esac
  else
    # We have to check if it's in "To_upstream" or "In_isolation".
    # By checking gateway we should be able to know that. However, if gatway is not allowed to ping?
    uplink_ip="$(LC_ALL=C get-all-nic-ip -g)"
    if [ "$uplink_ip" = "$gwadd_closed_lan" ]; then
      # Gateway is what we assigned, maybe it's a coincidence. Ping it
      echo "Testing if it's in isolated environment..."
      if ping -c 5 $uplink_ip &>/dev/null; then
        netenv_mode="To_upstream"
	# Do nothing since network setting has been done.
      else
        netenv_mode="In_isolation"
	# Still we have to configure dhcp service
        gen_lite_dhcpd_conf $dnsmasq_cfg
        service dnsmasq restart
	start_nat_service
      fi
    else
      # Gateway is NOT what we assigned. It must be To_upstream mode.
      netenv_mode="To_upstream"
    fi
  fi

  if [ "$ocs_restore_img_name" = "ask_user" -a \
       "$ocs_restore_dev" = "ask_user" ]; then
     interactive_mode="yes"
  fi

  MCAST_TMP=`mktemp /tmp/ocs_recovery_tmp.XXXXXX`
  trap "[ -f "$MCAST_TMP" ] && rm -f $MCAST_TMP" HUP INT QUIT TERM EXIT

  if [ -n "$(pidof udp-sender)" ]; then
    task_stop_feed_img_for_mcast
  fi

  # When $ocsroot is not a mount point, and nothing lists under it, run prep-ocsroot
  if [ "$interactive_mode" = "yes" ]; then
    if ! mountpoint $ocsroot &>/dev/null; then
      if [ -z "$(ls -d $ocsroot/*/ 2>/dev/null)" ]; then
        prep-ocsroot
      fi
    fi
  fi

  #
  if [ "$interactive_mode" = "yes" ]; then
    [ -z "$ocs_user_mode" ] && ask_if_beginner_or_expert_mode
  fi

  # Ask image name first (disk or parts image)
  # Image name
  if [ "$ocs_restore_img_name" = "ask_user" ]; then
    TMP=$(mktemp /tmp/menu.XXXXXX)
    trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
    $DIA \
    --backtitle "$msg_nchc_title" \
    --title "$msg_start_clonezilla_mode" \
    --menu "$msg_clonezilla_is_free_and_no_warranty\n$msg_choose_mode:" 0 0 0 \
    "restoredisk" "$msg_clonezilla_restore_disk" \
    "restoreparts" "$msg_clonezilla_restore_parts" \
    2> $TMP
    mcast_dev_action=$(cat $TMP)
    [ -f "$TMP" ] && rm -f $TMP
    # ocs_mode_prompt will be passed to the dialog about asking dev_type, OCS parameters...
    ocs_mode_prompt="$mcast_dev_action"
    case "$mcast_dev_action" in
    "restoredisk")
      ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
      trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
      get_existing_disk_image $ANS_TMP rest-unenc
      # the return name will be only one image name.
      target_dir="$(cat $ANS_TMP)"
      [ -e "$ANS_TMP" ] && rm -f $ANS_TMP
      dev_type="disk"
      ;;
    "restoreparts")
      ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
      trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
      get_existing_parts_image $ANS_TMP rest-unenc
      # the return name will be only one image name.
      target_dir="$(cat $ANS_TMP)"
      [ -e "$ANS_TMP" ] && rm -f $ANS_TMP
      dev_type="parts"
      ;;
    esac
    ocs_restore_img_name="$target_dir"  # Keep it for later use in run_again prompt
  else
    target_dir="$ocs_restore_img_name"
  fi
  check_input_target_image "$ocsroot/$target_dir"

  # Get target device
  if [ -z "$ocs_restore_dev" -o \
          "$ocs_restore_dev" = "ask_user" ]; then
    # choose the only dev we want
    case "$dev_type" in
     "disk")
        ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
        trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
	get_existing_disks_from_img $ANS_TMP $imagedir/$target_dir
        # we have to remove " (comes with checklist in dialog) so that for loop
        # will work (Specially for FC3/4...)
        target_hd="$(cat $ANS_TMP | tr -d \")"
        ocs_restore_dev="$target_hd" # keep it for later use in run_again prompt
        [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
        ;;
     "parts")
        ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
        trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
	get_existing_partitions_from_img $ANS_TMP $imagedir/$target_dir no restore
        # we have to remove " (comes with checklist in dialog) so that for loop
        # will work (Specially for FC3/4...)
        target_parts="$(cat $ANS_TMP | tr -d \")"
        ocs_restore_dev="$target_parts" # keep it for later use in run_again prompt
        [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
        ;;
    esac
  else
    if [ -n "$dev_type" ]; then
      # In interactive mode, we know $dev_type is.
      case "$dev_type" in
        "disk") target_hd="$ocs_restore_dev";;
        "parts") target_parts="$ocs_restore_dev";;
      esac
    else
      # When not in interactive mode, we have no idea what $dev_type is.
      # However, we can tell from $ocs_restore_dev, e.g. sda -> disk, sda1 -> parts
      # mcast_dev_action is required for function start_ocs_service
      # while target_hd or target_parts is required for function 
      # multicast_restoredisk and multicast_restoreparts (hence for start_ocs_service)
      first_ele="$(echo $ocs_restore_dev | awk -F" " '{print $1}')"
      if is_whole_disk $first_ele; then
        mcast_dev_action="restoredisk"
        target_hd="$ocs_restore_dev"
      else
        mcast_dev_action="restoreparts"
        target_parts="$ocs_restore_dev"
      fi
    fi
  fi

  # When Clonezilla live runs as multicast feeding server, the uplink port is where the packets sending.
  # No matter it's multicast, broadcast or bt, we use that ethernet port
  if [ -n "$eth_for_multicast" ]; then
    eth_for_deploy_packet="$eth_for_multicast"
  else
    eth_for_deploy_packet="$(LC_ALL=C get-all-nic-ip -u)"
  fi
  if [ -z "$eth_for_deploy_packet" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE | tee --append ${OCS_LOGFILE}
    echo "No network card was assigned for sending packets." | tee --append ${OCS_LOGFILE}
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL | tee --append ${OCS_LOGFILE}
    echo "$msg_program_stop!"
    exit 1
  fi
  mcast_srv_ip="$(LC_ALL=C drbl-get-ipadd $eth_for_deploy_packet)"

  if [ "$interactive_mode" = "yes" ]; then
    # ask if want to set ocs extra param
    OCS_PARAM_TMP=`mktemp /tmp/ocs_param_tmp.XXXXXX`
    trap "[ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP" HUP INT QUIT TERM EXIT
    set_drbl_ocs_extra_param -s -p reboot restore $OCS_PARAM_TMP $dev_type
    # OCS_OPT_PRE is for ocs-live-feed-img, we have to convert to that for ocs-sr
    OCS_OPT_PRE="$(cat $OCS_PARAM_TMP)"
    OCS_OPT_PRE="$(echo $OCS_OPT_PRE)"  # Make it in one line
    parse_ocs_live_feed_img_cmd_options_with_dash $OCS_OPT_PRE  # Obtain $OCS_OPT for ocs-sr
    [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
  fi
  # Load default settings if not assigned
  if [ -z "$chk_img_restoreable_on_srv" ]; then
    chk_img_restoreable_on_srv="$chk_img_restoreable_on_srv_def"
  fi

  # Enable bittorrent restoring when ocs-bttrack is available
  if type ocs-bttrack &>/dev/null; then
    bt_restore_msg_1="bittorrent"
    bt_restore_msg_2="$(rep_whspc_w_udrsc "$msg_bittorent_restore")"
  fi
  # get the mode of multicast, broadcast or bittorrent
  eval msg_choose_the_mode_to_restore_dev=\$msg_choose_the_mode_to_restore_${dev_type}
  if [ -z "$massive_deploy_mode" ]; then
    TMP=$(mktemp /tmp/menu.XXXXXX)
    trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
    $DIA \
    --backtitle "$msg_nchc_title" \
    --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
    --menu "$msg_choose_the_mode_to_restore_dev" 0 0 0 \
    "multicast"       "$msg_multicast_restore" \
    "broadcast"       "$msg_broadcast_restore" \
    $bt_restore_msg_1 $bt_restore_msg_2 \
    2> $TMP
    massive_deploy_mode=$(cat $TMP)
    [ -f "$TMP" ] && rm -f $TMP
  fi
  case "$massive_deploy_mode" in
    multicast|broadcast) castmode="multicast";;
             bittorrent) castmode="bt";;
		      *) echo "$msg_program_stop!"
			 exit 1
			 ;;
  esac
  [ "$massive_deploy_mode" = "broadcast" ] && udp_sender_extra_opt="$udp_sender_extra_opt --broadcast"
  OCS_OPT_PRE="$OCS_OPT_PRE -md $massive_deploy_mode"

  # ocs_mode_prompt will be passed to the dialog about asking mcast_wait_time, n_clients, mcast_max_wait_time
  ocs_mode_prompt="$massive_deploy_mode"
  if [ "$castmode" = "multicast" ]; then
    # eth_for_multicast will be passed to function start_ocs_service
    eth_for_multicast="$eth_for_deploy_packet" # passed to function start_ocs_service
    # Force to make client won't check the image integrity since this is for multicast restoring.
    OCS_OPT="$OCS_OPT -scr"
    task="multicast_restore" # pass to function get_bt_restore_mode_if_bittorrent
    OCS_OPT="$OCS_OPT --mcast-port $MULTICAST_PORT"

    if [ "$interactive_mode" = "yes" ]; then
      if [ -z "$n_clients" -o "$mcast_wait_time" ]; then
        get_multicast_restore_mode_if_mcast
        # Obtain mcast_wait_time, n_clients, mcast_max_wait_time (global variables) to pass to function start_ocs_service
      fi
    fi
    if [ -n "$n_clients" ]; then
      mcast_client_no_opt="-n $n_clients"  # Used for start_ocs_service
      OCS_OPT_PRE="$OCS_OPT_PRE --clients-to-wait $n_clients"  # For re-run prompt
    fi
    if [ -n "$mcast_wait_time" ]; then
      OCS_OPT_PRE="$OCS_OPT_PRE --time-to-wait $mcast_wait_time"  # For re-run prompt
    fi
    if [ -n "$mcast_max_wait_time" ]; then
      OCS_OPT_PRE="$OCS_OPT_PRE --max-time-to-wait $mcast_max_wait_time"  # For re-run prompt
    fi
  elif [ "$castmode" = "bt" ]; then
    # Force to make client won't check the image integrity since this is for multicast restoring.
    OCS_OPT="$OCS_OPT -scr"
    task="bt_restore" # pass to function get_bt_restore_mode_if_bittorrent
    if [ "$interactive_mode" = "yes" ]; then
      if [ -z "$n_clients" ]; then
        get_bt_restore_mode_if_bittorrent
        # Obtain n_clients (global variables) to pass to function start_ocs_service
      fi
    fi
    if [ -n "$n_clients" ]; then
      mcast_client_no_opt="-n $n_clients"  # Used for start_ocs_service
      OCS_OPT_PRE="$OCS_OPT_PRE --clients-to-wait $n_clients"  # For re-run prompt
    fi
  fi

  # Before really starting the feeding service, we can check if the image integrity is OK or not.
  # If the image is checked on the server, then we force to skip image checking on the clients when restoring.
  if [ -d "$imagedir/$target_dir" -a "$chk_img_restoreable_on_srv" = "yes" ]; then
    echo $msg_delimiter_star_line
    check_image_if_restorable -i "$target_dir" "$imagedir"
    rc_chkimg="$?"
    if [ "$rc_chkimg" -ne 0 ]; then
      echo "$msg_program_stop!"
      exit 1
    fi
  fi

  # For {multicast|bt}_restoredisk, the input device variable is $target_hd
  # For {multicast|bt}_restoreparts, the input device variable is $target_parts
  start_cmd="start_ocs_service $mcast_client_no_opt -f -t \"${castmode}_${mcast_dev_action}\" -o \"$target_dir $ocs_restore_dev\""
  echo "Running: $start_cmd"
  eval $start_cmd  # Do not put "| tee --append ${OCS_LOGFILE}". It will loop.
  rc=${PIPESTATUS[0]}
  if [ "$rc" -ne 0 ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "Faied to run $start_cmd" | tee --append ${OCS_LOGFILE}
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "$msg_program_stop!" | tee --append ${OCS_LOGFILE}
    exit 1
  fi

  if [ "$castmode" = "multicast" ]; then
    echo -n "Waiting for udp-sender service to be started... "
    time=$timeout_max
    while [ -z "$(LC_ALL=C ps -www --no-headers -C "udp-sender" -o pid,cmd)" ]; do
      sleep 1
      time="$((time - 1))"
      if [ "$time" -le "0" ]; then
        [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
        echo -n "Timeout for waiting for udp-sender process..." | tee --append ${OCS_LOGFILE}
        [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        echo "$msg_program_stop!" | tee --append ${OCS_LOGFILE}
        exit 1
      else
        echo -n "$time "
      fi
    done
    echo -e -n "Done!\n"
    echo "Feeding status:" | tee --append ${OCS_LOGFILE}
    echo $msg_delimiter_star_line | tee --append ${OCS_LOGFILE}
    ps -www -C "cat" -C "udp-sender" -o pid,cmd | tee --append ${OCS_LOGFILE}
    echo $msg_delimiter_star_line | tee --append ${OCS_LOGFILE}
    echo "More udp-sender and cat comamnds (if available) will be spawned when the 1st one is done." | tee --append ${OCS_LOGFILE}
  fi

  # Prepare the commands for clients
  if ! systemctl status lighttpd >/dev/null 2>&1; then
    systemctl start lighttpd | tee --append ${OCS_LOGFILE}
  fi

  echo $msg_delimiter_star_line
  # Two parts: img-wo-fs.tar.xz and ocs-client-run.sh
  # Part 1: img-wo-fs.tar.xz
  rm -f /var/www/html/ocs-client-run.sh /var/www/html/img-wo-fs.tar.xz | tee --append ${OCS_LOGFILE}
  echo "Preparing files for clients to download in /var/www/html/: ocs-client-run.sh, img-wo-fs.tar.xz" | tee --append ${OCS_LOGFILE}
  echo "Packing the tarball containing no file system images: img-wo-fs.tar.xz..." | tee --append ${OCS_LOGFILE}
  img_no_fs_tmp="$(mktemp -d /tmp/img_no_fs.XXXXXX)"
  rsync -a --exclude=*-ptcl-img.* --exclude=*.ntfs-img.* --exclude=*.dd-img.* $imagedir/$target_dir/* $img_no_fs_tmp | tee --append ${OCS_LOGFILE}
  if [ -d "$imagedir/btzone/$target_dir" ]; then
    mkdir -p $img_no_fs_tmp/btzone/$target_dir
    rsync -a $imagedir/btzone/$target_dir/*.torrent $img_no_fs_tmp/btzone | tee --append ${OCS_LOGFILE}
  fi
  # TODO: partimage file name, or drop it?
  for ifile in $imagedir/$target_dir/*-ptcl-img.* \
	       $imagedir/$target_dir/*.ntfs-img.* \
	       $imagedir/$target_dir/*.dd-img.*; do
    [ ! -e "$ifile" ] && continue  
    fn="$(basename $ifile)"
    touch $img_no_fs_tmp/$fn
  done
  ( cd $img_no_fs_tmp
    tar -cJf /var/www/html/img-wo-fs.tar.xz *
  )
  if [ -d "$img_no_fs_tmp" -a \
       -n "$(echo $img_no_fs_tmp | grep -i "img_no_fs")" ]; then
    rm -rf "$img_no_fs_tmp" | tee --append ${OCS_LOGFILE}
  fi
  # Part 2: ocs-client-run.sh
  cat <<-CLIENT_END > /var/www/html/ocs-client-run.sh
#!/bin/bash
DRBL_SCRIPT_PATH="\${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
. \$DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
. /etc/drbl/drbl-ocs.conf
. \$DRBL_SCRIPT_PATH/sbin/ocs-functions
[ -e "/etc/ocs/ocs-live.conf" ] && . /etc/ocs/ocs-live.conf

ask_and_load_lang_set
echo "Preparing the image info..."
rm -f /tmp/img-wo-fs.tar.xz
wget -P /tmp/ http://$mcast_srv_ip/img-wo-fs.tar.xz
if [ ! -e /tmp/img-wo-fs.tar.xz ]; then
  [ "\$BOOTUP" = "color" ] && \$SETCOLOR_FAILURE
  echo "Failed to retreive img-wo-fs.tar.xz from http://$mcast_srv_ip/."
  [ "\$BOOTUP" = "color" ] && \$SETCOLOR_NORMAL
  echo "Program terminated!"
  exit 1
fi
image="\$(mktemp -d /tmp/ocs-img.XXXXXX)"
tar -xJf /tmp/img-wo-fs.tar.xz -C \$image
if [ -d "\$image/btzone" ]; then
  mkdir -p /tmp/btzone/\$(basename \$image)
  mv \$image/btzone/*.torrent /tmp/btzone/\$(basename \$image)
fi
if [ -n "\$(ls -A \$image 2>/dev/null)" ]; then
  echo "Starting restoring..."
  ocs-sr --ocsroot /tmp/ -l en_US.UTF-8 $OCS_OPT ${castmode}_${mcast_dev_action} "\$(basename \$image)" $ocs_restore_dev
else
  [ "\$BOOTUP" = "color" ] && \$SETCOLOR_FAILURE
  echo "Failed to find the correct pseudo image dir \$image"
  [ "\$BOOTUP" = "color" ] && \$SETCOLOR_NORMAL
  echo "Program terminated!"
  exit 1
fi
CLIENT_END
  if [ -e "/var/www/html/img-wo-fs.tar.xz" -a -e "/var/www/html/ocs-client-run.sh" ]; then
    echo "Files /var/www/html/img-wo-fs.tar.xz and /var/www/html/ocs-client-run.sh created successfully:" | tee --append ${OCS_LOGFILE}
    ls -lv /var/www/html/img-wo-fs.tar.xz /var/www/html/ocs-client-run.sh | tee --append ${OCS_LOGFILE}
  else
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "Failed to create /var/www/html/img-wo-fs.tar.xz and /var/www/html/ocs-client-run.sh." | tee --append ${OCS_LOGFILE}
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "$msg_program_stop!" | tee --append ${OCS_LOGFILE}
    exit 1
  fi
  echo $msg_delimiter_star_line

  # Only in interactive mode we will show the command to run it again. For non-interactive mode, $OCS_OPT_PRE is not collected so the command is not complete.
  if [ "$interactive_mode" = "yes" ]; then
    run_again_fname="/tmp/ocs-live-feed-img-`date +%F-%H-%M`"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
    echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
    echo $ocs $OCS_OPT_PRE $mode $ocs_restore_img_name $ocs_restore_dev | tee $run_again_fname
    [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
    echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  fi
  echo $msg_delimiter_star_line
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  echo "$msg_boot_client_with_same_version_clonezilla_live: $mcast_srv_ip" | tee --append ${OCS_LOGFILE}
  echo "$msg_append_parameter_for_unattended_mode: locales=en_US.UTF-8 keyboard-layouts=us ocs_live_run=\"ocs-live-get-img $mcast_srv_ip\"" | tee --append ${OCS_LOGFILE}
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL

} # end of task_start_feed_img_for_mcast

task_stop_feed_img_for_mcast() {
  # 1. Kill process
  kill_ps_by_killall_9 udp-sender | tee --append ${OCS_LOGFILE}
  # Stop BT-related services
  ocs-btsrv -b stop
  # 2. Remove the file for clients.
} # task_stop_feed_img_for_mcast


####################
### Main program ###
####################

ocs_file="$0"
ocs=`basename $ocs_file`
OCS_LOGFILE="/var/log/${ocs}.log"

parse_ocs_live_feed_img_cmd_options_with_dash $*
shift ${n_shift}
mode="$1"
shift
ocs_restore_img_name="$1"
shift
ocs_restore_dev="$*"

# Fedora Core 1 seems to use dumb for rc1, we have to force it use linux.
# otherwise setterm will complain.
[ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="linux"
echo "Setting the TERM as $TERM"
export TERM="$TERM"

#
check_if_root
check_if_in_clonezilla_live
ask_and_load_lang_set

# check DIA
check_DIA_set_ESC $DIA

# Change to other mount point for extra harddisk
# Note: functions get_existing_disk_image, get_existing_parts_image and get_existing_partitions_from_img will use $imagedir 
if [ -n "$mount_point" ]; then
   echo "Using the image root directory $mount_point instead of $ocsroot." | tee --append ${OCS_LOGFILE}
   imagedir="$mount_point" 
else
   imagedir="$ocsroot"
fi

echo $msg_delimiter_star_line
if [ "$verbose" = "on" ]; then
  # The default output for udpcast stderr is surpressed, now turn it on
  udpcast_stderr="/dev/stderr"
fi

mkdir -p $ocs_log_dir
ocs_log_rotate $OCS_LOGFILE

case "$mode" in 
  start) task_start_feed_img_for_mcast;;
   stop) task_stop_feed_img_for_mcast;;
      *) USAGE;;
esac
