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

# input parameters:
# starthda1/startdisk/startparts is for OCS server to run

# save/savedisk/saveparts/restore/restoredisk/restoreparts/
# multicast_restore/multicast_restoredisk
# is for OCS client to run

# Notes:
# task_restorexxx/savexxx function will always use $ocsroot (/home/partimag) as mount point to do the save/restore, so it might overwrite the /home/partimag by mounting $ocsroot from server:/home2 (For instance.).

ocs_file="$0"
ocs=`basename $ocs_file`
ocs_myself_id="$$"
# log file for sfdisk when restoring
# RESTORE_SFDISK_LOG is loaded from drbl.conf

# Load DRBL setting and functions
if [ ! -f "/opt/drbl/sbin/drbl-conf-functions" ]; then
  echo "Unable to find /opt/drbl/sbin/drbl-conf-functions! Program terminated!" 
  exit 1
fi
. /opt/drbl/sbin/drbl-conf-functions

# Load some necessary setting from drbl-ocs.conf
. /opt/drbl/conf/drbl-ocs.conf

# Load basic functions of ocs
. /opt/drbl/sbin/ocs-functions

# 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

#
USAGE() {
    echo "Usage:"
    echo "To start clonezilla:"
    echo "$ocs [OPTION] {startdisk|startparts} {save|restore|multicast_restore}"
    echo " startdisk   operate on all partitions of the disk"
    echo " startparts  operate on the chosen partitions"
    echo " Options for restoring:"
    echo " -g, --grub-install GRUB_PARTITION     install grub in hda with root grub directory in GRUB_PARTITION when restoration finishs, GRUB_PARTITION can be one of \"/dev/hda1\", \"/dev/hda2\"... or \"auto\" (\"auto\" will clonezilla detects the grub root partition automatically)"
    echo " -r, --resize-partition   resize the partition when restoration finishes, this will try to fix the problem when small partition image is restored to larger partition. Now support partition with filesystem reiserfs, fat, ext2 and ext3. Warning!!! Use this carefully... Backup your data first."
    echo " -k, --no-fdisk   Do NOT run fdisk. If this option is set, you must make sure there is an existing partition table in the current restored harddisk. Default is Yes."
    echo " -t, --no-restore-mbr Do NOT restore the MBR (Mater Boot Record) when restoring image. If this option is set, you must make sure there is an existing MBR in the current restored harddisk. Default is Yes."
    echo " -u, --select-img-in-client  Input the image name in clients."
    echo " -e, --load-geometry  Force to use the saved CHS (cylinders, heads, sectors) when using sfdisk."
    echo " -y, --always-restore Let DRBL server as restore server, i.e. client will always has restore mode to choose (However default mode is local)."
    echo " --time-to-wait TIME  Even when the necessary amount of clients do have connected, still wait until TIME seconds since first client connection have passed."
    echo " --clients-to-wait NUMBER  Automatically start as soon as a minimal number of clients NUMBER have connected."
    echo " --max-time-to-wait TIME   When not enough clients have connected (but at least one), start anyways when TIME seconds since first client connection have pased. This option is used with --clients-to-wait."
    echo
    echo " Options for saving:"
    echo " -z0, --no-compress       don't compress when saving: very fast but very big image file (NOT compatible with multicast restoring!!!)"
    echo " -z1, --gzip-compress     compress using gzip when saving: fast and small image file (default)"
    echo " -z2, --bz2-compress      compress using bzip2 when saving: slow but smallest image file"
    echo " -z3, --lzo-compress      compress using lzop when saving: similar to the size by gzip, but faster than gzip. Only works with ntfsclone now"
    echo " -i, --image-size SIZE    set the split image file volume size SIZE (MB). Only works with partimage now"
    echo " -q, --use-ntfsclone      if the partition to be saved is NTFS, use program ntfsclone instead of partimage"
    echo
    echo " General options:"
    language_help_prompt_by_idx_no
    echo " -b, --batch              run $ocs in batch mode, i.e. without any prompt or wait to press enter"
    echo " -c, --confirm            wait for confirmation before restoring"
    echo " -d, --debug-mode         enter command mode to debug before saving/restoring"
    echo " --debug=LEVEL            output the partimage debug log in directory /var/log/ with debug LEVEL (0,1,2... default=0)"
    echo " -h, --hosts     IP_LIST  Instead of all DRBL clients, assign the clients by IP address, like: -h \"192.168.0.1 192.168.0.2\" NOTE!!! You must put \" \" before and after the IP_LIST!"
    echo " -s, --skip-hw-detect     skip the hardware detection (kudzu, harddrake or discover)"
    echo " -m, --module  MODULE     force to load kernel module MODULE, this is useful when some SCSI device is not detected."
    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 " -p, --postaction [poweroff|reboot|command]     when save/restoration finishs, poweroff, reboot (default) or enter command prompt"
    echo " -w, --wait-time TIME     wait for TIME secs before saving/restoring"
    echo " -mp, --mount-point MOUNT_POINT specify the other ocsroot directory instead of /home/partimag"
    echo " -nogui, --nogui          do not show GUI of partimage, use text only" 
    echo " -a, --no-force-dma-on    do not force to turn on HD DMA"
    echo " -v, --verbose            prints verbose information"
    echo
    echo "To stop clonezilla:"
    echo "$ocs stop"
}

# OCS_OPT is for drbl-ocs using, will not put into the partimage command options
# PARTIMAGE_RESTORE_OPT & PARTIMAGE_SAVE_OPT will put into partimage command options
PARTIMAGE_RESTORE_OPT=
PARTIMAGE_SAVE_OPT=
OCS_OPT=
# set the default values, we want to make sfdisk --force.
sfdisk_opt="--force"
nfs_restart="no"
always_restore="no"
mcast_loop="1"
udp_sender_extra_opt=""
USE_NTFSCLONE="no"
force_dma_on="yes"
# default output for udpcast stderr is surpressed.
# NOTE! Do not redirect it to standard output (stdin), otherwise partimage/ntfsclone pipe will get wrong image!
updcast_stderr="/dev/null"

# Parse command-line options
while [ $# -gt 0 ]; do
  case "$1" in
    -l|--language)
            shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      specified_lang="$1"
              shift
            fi
	    ;;
    -p|--postaction)  
            shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      postrun="$1"
              shift
            fi
	    # $postrun do not have to be put into OCS_OPT, since it will be
	    # converted and put into the S19ocs-run.
	    #OCS_OPT="$OCS_OPT -p \"$postrun\""
            ;;
    -g|--grub-install)  
            install_grub="on"
            shift
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              grub_partition=$1
              shift
            fi
            [ -z "$grub_partition" ] && USAGE && exit 1
	    OCS_OPT="$OCS_OPT -g $grub_partition"
            ;;
    -a|--no-force-dma-on)  
	    force_dma_on="no"
	    OCS_OPT="$OCS_OPT -n"
            shift;;
    -k|--no-fdisk)  
	    run_sfdisk="no"
	    OCS_OPT="$OCS_OPT -k"
            shift;;
    -t|--no-restore-mbr)  
	    restore_mbr="no"
	    OCS_OPT="$OCS_OPT -t"
            shift;;
    -u|--select-img-in-client)
	    select_img_in_client="yes"
	    OCS_OPT="$OCS_OPT -u"
            shift;;
    -e|--load-geometry)
	    load_HD_CHS="yes"
	    OCS_OPT="$OCS_OPT -e"
            shift;;
    -y|--always-restore)
	    always_restore="yes"
	    OCS_OPT="$OCS_OPT -y"
            shift;;
    -c|--confirm)
            confirm_restore="on"
	    OCS_OPT="$OCS_OPT -c"
	    shift;;
    -w|--wait-time)
            shift
            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
            fi
	    ;;
    -s|--skip-hw-detect)
            hw_detect="off"
	    OCS_OPT="$OCS_OPT -s"
	    shift;;
    --debug=?*)
            debug_level=${1#--debug=}
	    OCS_OPT="$OCS_OPT --debug=$debug_level"
	    shift;;
    -b|--batch)
            batch_mode="on"
	    shift;;
    -d|--debug-mode)
            debug_mode="on"
	    OCS_OPT="$OCS_OPT -d"
	    shift;;
    -h|--hosts)
	    LIST_HOST="on"
	    shift
            # skip the -xx option, in case 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
	      # parse if the arg is IP, skip those not IP
              if [ -n "$(echo $1 | grep -E "^[[:space:]]*([[:digit:]]+\.){3}[[:digit:]]+([[:space:]]|$)+")" ] ; then
                IP_LIST=$1
                shift
	      else
		echo "\"$1\" is not a suitable IP_LIST!!! Program terminated!!!"
		exit 1
              fi
            fi
	    ;;
    -m|--module)
            shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              module_to_load=$1
              shift
            fi
            [ -z "$module_to_load" ] && USAGE && exit 1
	    OCS_OPT="$OCS_OPT -m $module_to_load"
            ;;
    -n|--no-nfs-restart)
            # This function is deprecated, and it's not necesssary (since we already set nfs_restart="no"), but we have to be compatable with the old version. We will remove this in the future.
	    nfs_restart="no"
	    shift;;
    -f|--nfs-restart)
	    nfs_restart="yes"
	    shift;;
    -r|--resize-partition)
            resize_partition="on"
	    OCS_OPT="$OCS_OPT -r"
	    shift;;
    -v|--verbose)
	    verbose="on"
	    OCS_OPT="$OCS_OPT -v"
	    shift;;
    -z0|--no-compress)
            NTFSCLONE_CMP="cat"
	    PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -z0"
            OCS_OPT="$OCS_OPT -z0"
	    shift;;
    -z1|--gzip-compress)
            NTFSCLONE_CMP="gzip -c"
	    PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -z1"
            OCS_OPT="$OCS_OPT -z1"
	    shift;;
    -z2|--bz2-compress)
            NTFSCLONE_CMP="bzip2 -c"
	    PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -z2"
            OCS_OPT="$OCS_OPT -z2"
	    shift;;
    -z3|--lzo-compress)
            # Now only for ntfsclone, partimage not yet.
            NTFSCLONE_CMP="lzop -c"
	    # We will turn -z3 to -z1 for partimage later
	    PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -z3"
            OCS_OPT="$OCS_OPT -z3"
	    shift;;
    -nogui|--nogui)
            nogui="on"
            OCS_OPT="$OCS_OPT --nogui"
	    shift;;
    -mp|--mount-point)
            shift; 
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              mount_point=$1
	      shift
            fi
            [ -z "$mount_point" ] && USAGE && exit 1
	    OCS_OPT="$OCS_OPT --mount-point $mount_point" ; 
	    ;;
    -i|--image-size)
	    shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
              VOL_LIMIT=$1
	      shift
            fi
            [ -z "$VOL_LIMIT" ] && USAGE && exit 1
            OCS_OPT="$OCS_OPT -i $VOL_LIMIT"
	    ;;
    --time-to-wait)
	    shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      mcast_wait_time="$1"
	      shift
            fi
	    ;;
    --clients-to-wait)
	    shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      n_clients="$1"
	      shift
            fi
	    ;;
    --max-time-to-wait)
	    shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      mcast_max_wait_time="$1"
	      shift
            fi
	    ;;
    -x|--full-duplex)
            udp_sender_extra_opt="--full-duplex"
	    shift;;
    -q|--use-ntfsclone)
            OCS_OPT="$OCS_OPT --use-ntfsclone"
            USE_NTFSCLONE="yes"
	    shift;;
    -*)     echo "${0}: ${1}: invalid option" >&2
            USAGE >& 2
            exit 2 ;;
    *)      break ;;
  esac
done

[ -z "$VOL_LIMIT" ] && VOL_LIMIT=$VOL_LIMIT_DEFAULT
if [ -n "$(echo $VOL_LIMIT | grep -E "[^0-9]" 2>/dev/null)" ]; then
   [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
   echo "Image file size must be digits, not [$VOL_LIMIT]! Program terminated!"
   [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
   exit 1
fi

#
ask_and_load_lang_set $specified_lang

#
PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT --debug=$debug_level --volume=$VOL_LIMIT"
PARTIMAGE_RESTORE_OPT="$PARTIMAGE_RESTORE_OPT --debug=$debug_level"
if [ "$nogui" = "on" ]; then
  PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -B gui=no"
  PARTIMAGE_RESTORE_OPT="$PARTIMAGE_RESTORE_OPT -B gui=no"
fi
# Check
if [ -n "$(echo $PARTIMAGE_SAVE_OPT | grep "\-z3")" ]; then
   # Since partimage does not support lzo now, we will use gzip
   [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
   echo "Since partimage does not support lzo now, we will use gzip with partimage."
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
   PARTIMAGE_SAVE_OPT="$(echo $PARTIMAGE_SAVE_OPT | sed -e "s/-z3/-z1/g")"
fi
if [ "$USE_NTFSCLONE" = "yes" ] && ! type lzop &>/dev/null; then
   [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
   echo "lzop is NOT found in this server, we will use the default compression program (gzip)."
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
   NTFSCLONE_CMP="gzip -c"
fi

if [ "$verbose" = "on" ]; then
  echo "OCS_OPT: $OCS_OPT"
  echo "PARTIMAGE_RESTORE_OPT: $PARTIMAGE_RESTORE_OPT"
  echo "PARTIMAGE_SAVE_OPT: $PARTIMAGE_SAVE_OPT"
  # The default output for udpcast stderr is surpressed, now turn it on
  updcast_stderr="/dev/stderr"
fi

# ocs root
[ ! -d "$ocsroot" ] && mkdir -p $ocsroot

# change to other mount point for extra harddisk
if [ -n "$mount_point" ]; then
   imagedir="$mount_point" 
   echo "Using the mount point $imagedir."
else
   imagedir="$ocsroot"
fi

# check grub_partition if we want to install grub
if [ "$install_grub" = "on" ]; then
  if [ -z "`echo $grub_partition | grep "/dev/[sh]d[a-z][1-9]*"`" -a "$grub_partition" != "auto" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "\"$grub_partition\" is NOT a valid grub root partition! Program terminated!!!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    exit 1
  fi
fi

# convert to standard answer yes/no.
case "$run_sfdisk" in
   n|N|[nN][oO])
      run_sfdisk="no"
      ;;
   *)
      run_sfdisk="yes"
      ;;
esac
case "$restore_mbr" in
   n|N|[nN][oO])
      restore_mbr="no"
      ;;
   *)
      restore_mbr="yes"
      ;;
esac
case "$select_img_in_client" in
   y|Y|[yY][eE][sS])
      select_img_in_client="yes"
      ;;
   *)
      select_img_in_client="no"
      ;;
esac
echo $msg_delimiter_star_line
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
[ "$run_sfdisk" = "no" ] && echo "$msg_do_not_run_sfdisk"
[ "$restore_mbr" = "no" ] && echo "$msg_do_not_restore_mbr"
[ "$select_img_in_client" = "yes" ] && echo "$msg_you_have_to_input_image_names_in_client"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
echo $msg_delimiter_star_line

if [ -n "$TIME_to_wait" -a -z "`echo "$TIME_to_wait" | grep "[0-9].*"`" ]; then
   [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
   echo "\"$TIME_to_wait\" is NOT a valid time for wait-time! Program terminated!"
   [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
   exit 1
fi

#
stop_ocs_service() {
  local to_gen_ssi_files="$1"
  # check if "ocsmgrd" is running ?
  if [ ! -e "$ocsroot/clonezilla.lock" ]; then 
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "OCS is not started!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    exit 0
  fi

  # if $LIST_HOST=on, we should not affect the whole system...
  # HOST_OPTION_MODIFY: 
  if [ "$LIST_HOST" != "on" ]; then
    # $LIST_HOST !=on means we are setting all the clients.
    # affect the whole clients, BEGIN
    rm -f /etc/init.d/ocs
    rm -f $RCX_ROOTDIR/rc[06].d/S05ocs
    
    # kill drbl-ocs, ocsmgrd and udpcast daemon
    kill_drbl_ocs
    kill_ocsmgrd_daemon
    kill_udpSender_daemon
    
    # clean the tag
    rm -f $ocsroot/clonezilla.lock
    # affect the whole clients, END

    # restart nfs to make sure stalled NFS is cleaned.
    [ "$nfs_restart" != "no" ] && do_nfs_restart
  fi

  # inittab.ocs -> inittab
  # HOST_OPTION_MODIFY: modified since we specify hosts.

  # prepare the HOSTNAME-IP-MAC table
  OCS_TMP=`mktemp /tmp/ocs_clean_tmp.XXXXXX`
  trap "[ -f "$OCS_TMP" ] && rm -f $OCS_TMP" HUP INT QUIT TERM EXIT
  $DRBL_SCRIPT_PATH/bin/parse_dhcpd_conf $OCS_TMP
  for ihost in $drblroot/*; do
    IP="${ihost##/*/}"
    # if the LIST_HOST is on, skip those IP not listed in the $IP_LIST
    if [ "$LIST_HOST" = "on" ]; then
      [ -z "$(echo $IP_LIST | grep -E "\<$IP\>")" ] && continue
    fi
    echo "Stop OCS mode for node $IP, no matter it's in OCS mode or not."
    [ -f "$ihost/etc/inittab.ocs" ] && mv -f $ihost/etc/inittab.ocs $ihost/etc/inittab
    [ -f "$ihost/etc/ocs/clonezilla.conf" ] && rm -f $ihost/etc/ocs/clonezilla.conf
    # remove the pxe config file in $pxecfg_pd/tftpboot/pxelinux.cfg (IP-based)
    pxecfg="$($DRBL_SCRIPT_PATH/bin/gethostip.pl $IP)"
    [ -f "$PXELINUX_DIR/$pxecfg" ] && rm -f $PXELINUX_DIR/$pxecfg

    # HOST_OPTION_MODIFY: maybe it's not IP based, it's MAC based.
    # These files look like: 01-MAC address (with ":" -> "-"),
    pxecfg_MAC="01-$(grep ${ihost##*/} $OCS_TMP | awk -F" " '{print $3}' | tr ":" "-")"
    [ -f "$PXELINUX_DIR/$pxecfg_MAC" ] && rm -f $PXELINUX_DIR/$pxecfg_MAC

  done
  [ -f "$OCS_TMP" ] && rm -f $OCS_TMP

  # HOST_OPTION_MODIFY: modified since we specify hosts.
  # clean the services in rc1.d which are specially for drbl-ocs
  # The service name is both for RH-like, Debian or SuSE
  non_rc1_service="portmap nfslock netfs sshd kudzu harddrake ocs-run mountall.sh mountnfs.sh mountnfs.sh ssh discover hwscan"
  for ihost in $drblroot/*; do
    # if the LIST_HOST is on, skip those IP not listed in the $IP_LIST
    if [ "$LIST_HOST" = "on" ]; then
      [ -z "$(echo $IP_LIST | grep -E "\<${ihost##/*/}\>")" ] && continue
    fi

    for inon in $non_rc1_service; do
       rm -f $ihost/$RCX_ROOTDIR/rc1.d/S[0-9][0-9]${inon}
    done
  done
  # update the ssi image if it's in clonezilla box mode.
  # Loading the mode when drblpush, we need to know if it's clonezilla_box_mode
  # This only necessary in clonezilla server, since /etc/drbl/drbl_deploy.conf does not exist in client.
  . /etc/drbl/drbl_deploy.conf

  # To avoid, clientdir=node_root is missing.. we check here.
  # If it's clonezilla_box_mode, clean dhcpd leases, too.
  if [ "$clonezilla_box_mode" = "yes" ]; then
    echo $msg_delimiter_star_line
    [ "$to_gen_ssi_files" != "no" ] && $DRBL_SCRIPT_PATH/sbin/gen_ssi_files
    echo "Clean the dhcpd leases..."
    $DRBL_SCRIPT_PATH/sbin/clean-dhcpd-lease &>/dev/null
    echo $msg_delimiter_star_line
  fi

} # end of stop_ocs_servic

#
# start ocs services
# parameter: 
#   $1 task 
#      "save" for saving the data to server
#      "restore" for restoring the data from server  
#   $2 option
#      "$target_image" for save/restore the partition /dev/hda1
#      "$target_dir $target_hd" for save/restore the disk
#   $3 n_clients
#      how many clients will be connected (for multicast only)
start_ocs_service() {
  local task="$1"
  local option="$2"
  local n_clients="$3"

  # is "ocsmgrd" running ?
  if [ -e "$ocsroot/clonezilla.lock" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "Your system is already in clonezilla mode... we will stop it first if necessary, then start it again..."
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    # We do not have to generate SSI files in start_ocs_service, since later the files will be created.
    stop_ocs_service no
  fi

  if [ ! -e "$SYSCONF_PATH/$DHCP_SRV_NAME" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "You must setup DRBL first. Check http://drbl.sf.net or http://drbl.nchc.org.tw for more details."
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  fi

  # Warning if range option is used in /etc/dhcpd.conf
  check_dhcpd_if_range

  # restart nfs to make sure stalled NFS is cleaned.
  [ "$nfs_restart" != "no" ] && do_nfs_restart

  # Loading the mode when drblpush, we need to know if it's clonezilla_box_mode
  # This only necessary in clonezilla server, since /etc/drbl/drbl_deploy.conf does not exist in client.
  . /etc/drbl/drbl_deploy.conf

  # To avoid, clientdir=node_root is missing.. we check here.
  # If it's clonezilla_box_mode, clean dhcpd leases, too.
  if [ "$clonezilla_box_mode" = "yes" ]; then
    echo $msg_delimiter_star_line
    echo "You are in clonezilla box mode!"
    $DRBL_SCRIPT_PATH/sbin/turn-drbl-ssi-mode on 
    echo "Clean the dhcpd leases..."
    $DRBL_SCRIPT_PATH/sbin/clean-dhcpd-lease &>/dev/null
    echo $msg_delimiter_star_line
  fi

  # HOST_OPTION_MODIFY: modified since we specify hosts.
  # make sure next time when server boots, it is NOT in ocs mode
  cat <<-EOF > /etc/init.d/ocs
#!/bin/bash
# $drblroot $PXELINUX_DIR will be converted when output to /etc/init.d/ocs
for host in $drblroot/*; do
  [ -f "\$host/etc/inittab.ocs" ] && mv -f \$host/etc/inittab.ocs \$host/etc/inittab
done

# remove all the pxe config file in $PXELINUX_DIR/ except 
# "$PXELINUX_DIR/default"
echo -n "Clean all the previous OCS saved config file if they exist..."
for ifile in $PXELINUX_DIR/*; do
  [ "\$ifile" != "$PXELINUX_DIR/default" -a -f "\$ifile" ] && rm -f \$ifile
done

rm -f $ocsroot/clonezilla.lock
rm -f /etc/init.d/ocs
rm -f $RCX_ROOTDIR/rc0.d/S05ocs
rm -f $RCX_ROOTDIR/rc6.d/S05ocs
EOF
  chmod 755 /etc/init.d/ocs
  ln -fs /etc/init.d/ocs $RCX_ROOTDIR/rc6.d/S05ocs
  ln -fs /etc/init.d/ocs $RCX_ROOTDIR/rc0.d/S05ocs

  # get the multicast ethernet port
  if [ -n "$(echo "$task" | grep -e "^multicast_restore")" ]; then
    find_multicast_ethernet_port
  fi

  # HOST_OPTION_MODIFY: modified since we specify hosts.
  # inittab -> inittab.ocs
  for ihost in $drblroot/*; do
    # if the LIST_HOST is on, skip those IP not listed in the $IP_LIST
    if [ "$LIST_HOST" = "on" ]; then
      [ -z "$(echo $IP_LIST | grep -E "\<${ihost##/*/}\>")" ] && continue
    fi
    echo "Starting the OCS service for node IP = ${ihost##/*/}"
    # inittab -> inittab.ocs
    # check if inittab.ocs exist ?
    if [ ! -e "$ihost/etc/inittab.ocs" ]; then
      # backup the original one
      cp -f $ihost/etc/inittab $ihost/etc/inittab.ocs
    fi
    # inittab for ocs
    if [ -n "$(echo "$task" | grep -e "^multicast_restore")" ]; then
      multicast_option="$MULTICAST_PORT $option"
      ocs_inittab $ihost/etc/inittab $task "$multicast_option"
    else
      ocs_inittab $ihost/etc/inittab $task "$option"
    fi

    # create the always_restore tag
    # clean the stall one first.
    rm -f $ihost/etc/ocs/clonezilla.conf
    if [ "$always_restore" = "yes" ]; then
      mkdir -p $ihost/etc/ocs/
      echo "always_restore=yes" > $ihost/etc/ocs/clonezilla.conf
    fi
  done

  # Set the single user mode password if not setting for client...This will be safer..."
  set_clients_rc1_passwd $IP_LIST

  # HOST_OPTION_MODIFY: 
  # before start the ocsmgrd, kill the stale one
  kill_ocsmgrd_daemon

  # make client to boot in drbl mode with menu label specified.
  case "$task" in
    "saveparts")
       if [ "$target_parts" = "ask_user"  -a "$target_image" = "ask_user" ]; then
         ocs_menu_label="save partitions (choose later) as image (input later)"
       elif [ -n "$target_parts" -a -n "$target_image" ]; then
         ocs_menu_label="save partitions $target_parts as image $target_image"
       else
         ocs_menu_label="save partitions"
       fi
       ;;
    "savedisk")
       if [ "$target_hd" = "ask_user" -a "$target_image" = "ask_user" ]; then
         ocs_menu_label="save disk (choose later) as image (input later)"
       elif [ -n "$target_hd" -a -n "$target_image" ]; then
         ocs_menu_label="save disk $target_hd as image $target_image"
       else
         ocs_menu_label="save disk"
       fi
       ;;
    "restoreparts")
       # convert option to target_dir, (option: target_dir hd_list)
       target_dir="$(echo "$option" | awk -F' ' '{print $1}')"
       if [ "$target_dir" = "ask_user" ]; then
         ocs_menu_label="unicast restore an image (select later) to partitions"
       else
         ocs_menu_label="unicast restore $target_dir to partitions"
       fi
       ;;
    "restoredisk")
       # convert option to target_dir, (option: target_dir hd_list)
       target_dir="$(echo "$option" | awk -F' ' '{print $1}')"
       if [ "$target_dir" = "ask_user" ]; then
         ocs_menu_label="unicast restore an image (select later) to disk"
       else
         ocs_menu_label="unicast restore $target_dir to disk"
       fi
       ;;
    "multicast_restoredisk")
       # convert option to target_dir, (option: target_dir hd_list)
       target_dir="$(echo "$option" | awk -F' ' '{print $1}')"
       ocs_menu_label="multicast restore $target_dir to disk"
       ;;
    "multicast_restoreparts")
       # convert option to target_dir, (option: target_dir hd_list)
       target_dir="$(echo "$option" | awk -F' ' '{print $1}')"
       ocs_menu_label="multicast restore $target_dir to partitions"
       ;;
     *)
  esac

  force_pxe_clients_boot_drbl "Clonezilla: $ocs_menu_label"
  # set runlevel 1 to kernel parameter in pxelinux config
  add_runlevel_1_in_pxelinux_cfg_drbl_block

  # if the mode is in always_restore, we will set local boot as default. Since
  # the always_restore mode is only useful with local OS exists.
  if [ "$always_restore" = "yes" ]; then
    force_pxe_clients_boot_local
  fi

  # start the ocsmgrd daemon
  # the $ocsroot/clonezilla.lock is a tag for ocs is running,
  # after start/stop cloning, we will remove $ocsroot/clonezilla.lock
  touch $ocsroot/clonezilla.lock
  # UDPCAST MODIFY
  $DRBL_SCRIPT_PATH/sbin/ocsmgrd &
  ocs_log_file="$(grep "joblogfile =>" /opt/drbl/sbin/ocsmgrd | awk -F" " '{print $3}')"
  echo "$msg_client_job_are_logged_in $ocs_log_file"
  if [ -n "$(echo $task | grep -i "restore")" ]; then
    # if the mode restore, show the log prompt
    echo "$msg_client_sfdisk_log_are_in $RESTORE_SFDISK_LOG"
  fi

  # Multicast part
  # set udpcast holding option
  if [ -n "$(echo "$task" | grep -e "^multicast_restore")" ]; then
    # prepare the multicast option
    [ -n "$mcast_wait_time" ] && udpcast_hold_opt1="--min-wait $mcast_wait_time" 
    [ -n "$n_clients" ] && udpcast_hold_opt2="--min-clients $n_clients"
    [ -n "$mcast_max_wait_time" ] && udpcast_hold_opt3="--max-wait $mcast_max_wait_time" 
    [ -z "$udpcast_hold_opt1" -a -z "$udpcast_hold_opt2" -a -z "$udpcast_hold_opt3" ] && echo "Someting went wrong! I can not find time_to_wait, clients_to_wait or clients+time_to_wait option! Program terminated!" && exit 1
  fi

  #
  trap "exit 1" HUP INT QUIT TERM EXIT
  target_dir="$(echo "$option" | awk -F' ' '{print $1}')"
  case "$task" in
    "multicast_restoredisk")
      # find the available partitions
      target_parts=""
      # Note! $target_dir is not absolute path, because when task_saveparts do the real job later, it will add $ocsroot, so we have to put $ocsroot here.
      for partition in `get_known_partition $imagedir/$target_dir/pt.sf`; do
        target_parts="$target_parts $partition"
      done
      # Let feed_multicast_restoreparts do the real job
      # target_dir will be got from $option when run feed_multicast_restoreparts
      feed_multicast_restoreparts "$target_dir" "$target_parts" &
      ;;

    "multicast_restoreparts")
      # Let feed_multicast_restoreparts do the real job
      #feed_multicast_restoreparts "$option" &
      feed_multicast_restoreparts "$target_dir" "$target_parts" &
      ;;
  esac

  # We do not have to run /opt/drbl/sbin/gen_ssi_files in the end of start_ocs_service is because we will always update the /etc/rc1.d for client (mapping to /tftpboot/node_root/drbl_ssi/rc1.d in the server) when a client boots in SSI mode.
}
# end of start_ocs_service

# create inittab for ocs
ocs_inittab() {
  # Usage:
  # ocs_inittab $drblroot/$host/etc/inittab $task "$option"
  # Then it will run like this:
  # /sbin/drbl-ocs restoredisk /home/partimag/woody_base hda
  local inittab=$1
  local inittab_task=$2
  local inittab_option=$3

  # change the mode to rc1.d
  perl -p -i -e "s/^id:[1-5]:initdefault:/id:1:initdefault:/g" $inittab
  CLIENT="$(dirname $inittab)"
  # prepare some services
  # Note!!! This functions must be consistent with "non_rc1_service" in stop()
  # S00single (RH), S20single (Debian), so we have to put these services
  # before 20.
  (
   cd $CLIENT/../$RCX_ROOTDIR/rc1.d 
   # RH-like: nfslock, netfs...
   # Debian: ssh, nfs-common, mountall.sh...
   # SUSE: nfs, but nfs is server for RH-like...
   if [ -e /etc/debian_version ]; then
     # Debian
     [ -x $RCX_REL_INITD/portmap ] && ln -fs $RCX_REL_INITD/portmap S13portmap 
     [ -x $RCX_REL_INITD/nfs-common ] && ln -fs $RCX_REL_INITD/nfs-common S15nfs-common
     [ -x $RCX_REL_INITD/ssh ] && ln -fs $RCX_REL_INITD/ssh S16ssh
     [ -x $RCX_REL_INITD/mountall.sh ] && ln -fs $RCX_REL_INITD/mountall.sh S17mountall.sh
     [ -x $RCX_REL_INITD/mountvirtfs ] && ln -fs $RCX_REL_INITD/mountvirtfs S17mountvirtfs
     [ -x $RCX_REL_INITD/mountnfs.sh ] && ln -fs $RCX_REL_INITD/mountnfs.sh S17mountnfs.sh
   elif [ -e /etc/SuSE-release ]; then
     # SuSE
     [ -x $RCX_REL_INITD/kbd ] && ln -fs $RCX_REL_INITD/kbd S12kbd
     [ -x $RCX_REL_INITD/portmap ] && ln -fs $RCX_REL_INITD/portmap S13portmap 
     [ -x $RCX_REL_INITD/nfs ] && ln -fs $RCX_REL_INITD/nfs S15nfs
     [ -x $RCX_REL_INITD/sshd ] && ln -fs $RCX_REL_INITD/sshd S16sshd
   else
     # RH-like
     [ -x $RCX_REL_INITD/portmap ] && ln -fs $RCX_REL_INITD/portmap S13portmap 
     [ -x $RCX_REL_INITD/nfslock ] && ln -fs $RCX_REL_INITD/nfslock S14nfslock 
     [ -x $RCX_REL_INITD/netfs ] && ln -fs $RCX_REL_INITD/netfs S15netfs 
     [ -x $RCX_REL_INITD/sshd ] && ln -fs $RCX_REL_INITD/sshd S16sshd
   fi
  )
  # we need kudzu or harddrake so that for SCSI devices, the driver can be loaded auto.
  if [ "$hw_detect" != "off" ]; then
    for ihw in kudzu harddrake discover hwscan; do
     (
      cd $CLIENT/../$RCX_ROOTDIR/rc1.d 
      [ -x "$RCX_REL_INITD/$ihw" ] && ln -fs $RCX_REL_INITD/$ihw S05${ihw}
     )
    done
  fi

  # create the ocs-run service to run partimage
  [ -n "$module_to_load" ] && LOAD_MOD="modprobe $module_to_load"
  cat <<-EOF > $CLIENT/../$RCX_ROOTDIR/rc1.d/S19ocs-run
#!/bin/bash
# Set the locale to avoid the dialog distortion.
if [ -e /etc/debian_version ]; then
  # Debian
  # For B2D, it uses /etc/sysconfig/i18n...
  [ -e /etc/sysconfig/i18n ] && . /etc/sysconfig/i18n
  [ -e /etc/environment ] && . /etc/environment
elif [ -e /etc/SuSE-release ]; then
  # SuSE
  [ -e /etc/SuSEconfig/profile ] && . /etc/SuSEconfig/profile
else
  # RH-like
  [ -e /etc/sysconfig/i18n ] && . /etc/sysconfig/i18n
fi
# Force to set LANG=C so that we can see yes/no in dialog...
export LANG=C
$LOAD_MOD
/opt/drbl/sbin/$ocs $OCS_OPT --language 0 $inittab_task $inittab_option

# Restore the inittab so that if ocsmgrd fails to get the message, the client
# will not clone again.
[ -e /etc/ocs/clonezilla.conf ] && . /etc/ocs/clonezilla.conf
if [ "$always_restore" != "yes" ]; then
  [ -f "/etc/inittab.ocs" ] && mv -f /etc/inittab.ocs /etc/inittab
fi
$postrun
EOF
  chmod 755 $CLIENT/../$RCX_ROOTDIR/rc1.d/S19ocs-run
}

# task_notify_localboot
# ask for localboot next time
task_notify_localboot() {
  [ -e /etc/ocs/clonezilla.conf ] && . /etc/ocs/clonezilla.conf
  if [ "$always_restore" = "yes" ]; then
     echo "It's in always-restore clonezilla mode!"
     echo "Do not notify clonezilla server to switch to local boot mode."
     echo $msg_delimiter_star_line
     echo "Finished!"
     return 2
  fi

  echo $msg_delimiter_star_line
  echo -n "Notifying clonezilla server my job is done... "
  netdevices="$($DRBL_SCRIPT_PATH/bin/get-nic-devs)"
  for device in $netdevices; do
    ip="$($DRBL_SCRIPT_PATH/bin/get_ip $device)"
    [ -z "$ip" ] && continue
    ocs_server="$($DRBL_SCRIPT_PATH/bin/get_nfsserver $ip)"
    # If ocsserver is not found, then this NIC is not for DRBL, try another one.
    [ -z "$ocs_server" ] && continue
    mac="$($DRBL_SCRIPT_PATH/bin/get_mac $device)"
    break
  done
  # 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.
  ran="$(echo | awk "{ srand(); print rand() }")"
  time="$(echo "scale=0; $NOTIFY_OCS_SERVER_TIME_LIMIT * $ran / 1" |bc -l)"
  countdown $time
  echo
  echo -n "Sending info \"$ip $mac\" to $ocs_server:$OCSMGRD_PORT... "
  $DRBL_SCRIPT_PATH/bin/socket.pl -m "$ip $mac" -h $ocs_server -p $OCSMGRD_PORT
  echo "done!"
  echo $msg_delimiter_star_line
  echo "Finished!"
} # end of task_notify_localboot

# task "savedisk"
# parameter:
#  $1 target_dir
#  $2 target_hd
task_savedisk() {
  local target_dir=$1
  local target_hd=$2
  local target_parts=$2

  screen_not_blank
  active_proc_partitions
  #######################################################
  # 2003/05/12: ask user about target_dir and target_hd #
  #######################################################
  if [ "$target_dir" = "ask_user" ]; then
    ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
    trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
    get_input_image_name $ANS_TMP $target_part
    target_dir=$(cat $ANS_TMP)
    [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
    # Note! We won't add leading $ocsroot in $target_dir, because when task_saveparts do the real job later, it will add that.
  fi
   
  check_existing_target_image "$ocsroot/$target_dir"

  if [ "$target_hd" = "ask_user" ]; then
    ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
    trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
    get_input_dev_name $ANS_TMP harddisk
    target_hd="$(cat $ANS_TMP)"
    [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
    check_input_hd $target_hd
  fi

  # check if the device exists
  ANS_TMP=`mktemp /tmp/ocs_chkdev.XXXXXX`
  trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
  check_if_input_device_exist $ANS_TMP $target_hd 
  target_hd="$(cat $ANS_TMP)"
  [ -f "$ANS_TMP" ] && rm -f $ANS_TMP

  # We need the partition table to conver the selected HD to partitions.
  pt_tmp="$(mktemp /tmp/pt_tmp.XXXXXX)"
  sfdisk -d /dev/$target_hd > $pt_tmp

  # find the available partitions
  target_parts=""
  for partition in `get_known_partition $pt_tmp`; do
    target_parts="$target_parts $partition"
  done
  [ -f "$pt_tmp" ] && rm -f $pt_tmp

  # Let task_saveparts do the real job
  task_saveparts $target_dir "$target_parts"

  # The target_dir is local variable, so we put leading $ocsroot to save the disk
  # disk is an important tag to mark that image is for entire disk, and its content consists of the save disk device name (hda, hdb...)
  echo "$target_hd" > $ocsroot/$target_dir/disk
}

# task "saveparts"
# parameter:
#  $1 target_dir
#  $2 target_parts
task_saveparts() {
  local target_dir=$1
  local target_parts=$2

  # target_hd will be extract from $target_parts, maybe we will have one more
  local target_hd=""
  local hd_tmp=""

  screen_not_blank
  active_proc_partitions
  #######################################################
  # ask user about target_dir and target_parts #
  #######################################################
  if [ "$target_dir" = "ask_user" ]; then
    ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
    trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
    get_input_image_name $ANS_TMP $target_part
    target_dir=$(cat $ANS_TMP)
    [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
  fi
   
  # set $target_dir as the absolute path, i.e. put leading $ocsroot.
  target_dir="$ocsroot/$target_dir"
  check_existing_target_image $target_dir

  if [ "$target_parts" = "ask_user" ]; then
    ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
    trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
    get_input_dev_name $ANS_TMP partition
    # we have to remove " (comes with checklist in dialog) so that for loop
    # will work (Specially for FC3/4...)
    target_parts="$(cat $ANS_TMP | tr -d \")"
    [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
    check_input_partition $target_parts
  fi

  # check if the device exists
  ANS_TMP=`mktemp /tmp/ocs_chkdev.XXXXXX`
  trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
  check_if_input_device_exist $ANS_TMP $target_parts
  # we have to remove " (comes with checklist in dialog) so that for loop
  # will work (Specially for FC3/4...)
  target_parts="$(cat $ANS_TMP | tr -d \")"
  [ -f "$ANS_TMP" ] && rm -f $ANS_TMP

  # find the target hd
  # maybe we will have one more hd (like hda1, hda2, hdb1, hdb3 -> hda, hdb)
  for ipart in $target_parts; do
    hd_tmp=${ipart:0:3}
    if [ -z "$target_hd" ]; then
	    target_hd="$hd_tmp"
    elif [ -z "$(echo $target_hd | grep -E "$hd_tmp" 2>/dev/null)" ]; then
	    target_hd="$target_hd $hd_tmp"
    fi
  done
  # turn on hd dma 
  for ihd in $target_hd; do
    check_specify_hd_exists $ihd
    [ "$force_dma_on" = "yes" ] && turn_on_hd_dma /dev/$ihd
  done
  # swapoff -a, just in case
  if [ -e /etc/debian_version -o -e /etc/SuSE-release ]; then
    # Debian
    # 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

  mkdir -p $target_dir
  echo -n "Reading the partition table..."
  sfdisk -d /dev/$target_hd > $target_dir/pt.sf
  RETVAL="$?"
  echo "RETVAL=$RETVAL"
  clean_cylinder_boundary_warning $target_dir/pt.sf
  output_HD_CHS $target_hd $target_dir
  if [ $RETVAL -ne 0 ]; then
    sfdisk_fail_process
    exit 1
  fi
  echo "done!"

  echo "Reading the MBR and partition table..."
  dd if=/dev/$target_hd of=$target_dir/mbr count=1 bs=512

  do_LVM_save="no"
  for partition in $target_parts; do
    check_LVM_partition /dev/$partition
    if [ "$?" -eq 0 ]; then
      # LVM
      # We have to do save LVM (PV/VG/LV) together, not follow every partition
      do_LVM_save="yes"
      continue
    fi
    # not LVM, normal partition
    checkInodeForDevice /dev/$partition 
    partimage_save /dev/$partition $target_dir/$partition
  done
  # We have to do save LVM (PV/VG/LV) together, not follow every partition
  if [ "$do_LVM_save" = "yes" ]; then
    save_logv "$target_parts"
  fi
  echo "$target_parts" > $target_dir/parts
}

# task "restoredisk" 
# parameter:
#   $1 target_image
#   $2 target_hd
task_restoredisk() {
  local target_dir="$1"
  local target_hd="$2"
  local port="$3"

  case "$target_dir" in
    "ask_user")
        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
        # the return name will be only one image name.
        target_dir="$(cat $ANS_TMP)"
        [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
        # Note! We won't add leading $ocsroot in $target_dir, because when task_saveparts do the real job later, it will add that.
        ;;
  esac
  check_input_target_image "$ocsroot/$target_dir"

  case "$target_hd" in
    "ask_user")
        target_hd=$(cat $ocsroot/$target_dir/disk 2>/dev/null)
        ;;
  esac

  # find the available partitions
  target_parts=""
  # Note! $target_dir is not absolute path, because when task_saveparts do the real job later, it will add $ocsroot, so we have to put $ocsroot here.
  for partition in `get_known_partition $ocsroot/$target_dir/pt.sf`; do
    target_parts="$target_parts $partition"
  done

  # Let task_restoreparts do the real job
  task_restoreparts $target_dir "$target_parts" $port
}

# task "multicast_restoredisk" 
# parameter:
#   $1 port
#   $2 target_dir
#   $3 target_hd
task_multicast_restoredisk() {
  local port=$1
  local target_dir="$2"
  local target_hd=$3
  # To do backward compatability, we still keep task_multicast_restoredisk function, but let task_restoredisk do the real job.
  task_restoredisk $target_dir "$target_hd" $port
}

# task "restoreparts" 
# parameter:
#   $1 target_image
#   $2 target_parts (hda1, hda2...)
#   $3 port (if exists, it's multicast mode).
task_restoreparts() {
  local target_dir="$1"
  local target_parts="$2"
  # we use the port to identify it's unicast or multicast.
  local port="$3"

  case "$target_dir" in
    "ask_user")
       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
       # the return name will be only one image name.
       target_image="$(cat $ANS_TMP)"
       # target_image is an important global variable, it's related to the PXE menu tag, and some other important thing.
       [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
       ;;
  esac
  # set $target_dir as the absolute path, i.e. put leading $ocsroot.
  target_dir="$ocsroot/$target_dir"
  check_input_target_image "$target_dir"

  case "$target_parts" in
    "ask_user")
       ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
       trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
       get_existing_partitions $ANS_TMP $target_image
       # we have to remove " (comes with checklist in dialog) so that for loop
       # will work (Specially for FC3/4...)
       target_parts="$(cat $ANS_TMP | tr -d \")"
       [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
       ;;
  esac

  # we use the port to identify it's unicast or multicast.
  if [ -n "$port" ]; then
    net_mode="multicast"
  else
    net_mode="unicast"
  fi

  # target_hd will be extract from $target_parts, maybe we will have one more
  local target_hd=""
  local hd_tmp=""

  # find the target hd
  # maybe we will have one more hd (like hda1, hda2, hdb1, hdb3 -> hda, hdb)
  for ipart in $target_parts; do
    hd_tmp=${ipart:0:3}
    if [ -z "$target_hd" ]; then
	    target_hd="$hd_tmp"
    elif [ -z "$(echo $target_hd | grep -E "$hd_tmp" 2>/dev/null)" ]; then
	    target_hd="$target_hd $hd_tmp"
    fi
  done
  # turn on hd dma 
  for ihd in $target_hd; do
    check_specify_hd_exists $ihd
    [ "$force_dma_on" = "yes" ] && turn_on_hd_dma /dev/$ihd
  done
  screen_not_blank
  active_proc_partitions

  # if $run_sfdisk is no, only some preparations in create_partition, it won't
  # run sfdisk in fact.
  [ "$load_HD_CHS" = "yes" ] && load_HD_geometry $target_dir
  create_partition /dev/$target_hd $target_dir/pt.sf "$sfdisk_opt"

  do_LVM_restore="no"
  for partition in $target_parts; do
    if [ -n "$(grep -E "^/dev/$partition" $target_dir/pt.sf | grep -i "Id=8e")" ]; then
      # We have to do restore LVM (PV/VG/LV) together, not follow every partition
      do_LVM_restore="yes"
      continue
    fi
    checkInodeForDevice /dev/$partition
    case "$net_mode" in 
     "unicast")
        do_unicast_stdin_restore $target_dir $partition /dev/$partition
        rc="$?"
        ;;
     "multicast")
        do_multicast_udpcast_restore $target_dir $partition /dev/$partition
        rc="$?"
        ;;
    esac
    if [ $rc -gt 0 ]; then
       [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
       echo "Failed to restore partition image file $target_dir/${partition}* to /dev/$partition! Maybe this image is corrupt! $msg_press_enter_to_continue..."
       [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
       read
    fi
  done
  # swap partition
  for partition in `cat $target_dir/pt.sf | grep "Id=82" | cut -d" " -f1 | sed -e 's/\/dev\///g'`
  do
    echo "Found the swap partition /dev/$partition, creat it:"
    mkswap /dev/$partition
    echo $msg_delimiter_star_line
  done

  # We have to do restore LVM (PV/VG/LV) together, not follow every partition
  if [ "$do_LVM_restore" = "yes" ]; then
    # LVM exists, restore PV/VG/LV.
    restore_logv "$target_parts" $net_mode $port
    echo $msg_delimiter_star_line
  fi

  # re-install MBR
  if [ "$restore_mbr" = "yes" ]; then
    echo -n "Restoring the MBR data... "
    dd if=$target_dir/mbr of=/dev/$target_hd count=1 bs=512 &>/dev/null
    echo "done!"
    echo $msg_delimiter_star_line
  fi

  # re-install grub here
  if [ "$install_grub" = "on" ]; then
    install_grub_hd $grub_partition
    echo $msg_delimiter_star_line
  fi

  # resize partition if necessary
  if [ "$resize_partition" = "on" ]; then
    resize_partition $target_hd
    echo $msg_delimiter_star_line
  fi
} # end of task_restoreparts

# task "multicast_restoreparts" 
# parameter:
#   $1 port
#   $2 target_dir
#   $3 target_hd
task_multicast_restoreparts() {
  local port=$1
  local target_dir="$2"
  local target_parts="$3"
  # To do backward compatability, we still keep task_multicast_restoreparts function, but let task_restoreparts do the real job.
  task_restoreparts $target_dir "$target_parts" $port
}

check_boot_kernel_arg_cmdline() {
  # The mode assigned when client boots, if this is assigned, 
  # it will overide the mode assigned by server
  if grep -i -q "drbl_ocs=disallow" /proc/cmdline; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "DRBL server assigned drbl_ocs=\"disallow\" for this client, so the clonezilla execution in this machine is aborted!"
    echo -n "$msg_press_enter_to_continue..."
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    read
    exit 0
  fi
}

#
kill_ocsmgrd_daemon() {
    # kill the ocsmgrd daemon
    pid=`ps -e | grep 'ocsmgrd'`
    [ -n "$pid" ] && killall ocsmgrd
}   

kill_drbl_ocs() {
    # kill drbl-ocs
    # pids="$(ps -e -o pid,args | awk -F" " '/drbl-ocs/ {print $1}')"
    pids="$(ps -e -o pid,args | grep -E "$ocs" | grep -v "grep" | awk -F" " '{print $1}')"
    if [ -n "$pids" ]; then
      for p in $pids; do
        [ "$p" != "$ocs_myself_id" ] && kill -9 $p &> /dev/null
      done
    fi
}   

kill_udpSender_daemon() {
    # kill_udp-sender_daemon
    pid=`ps -e | grep 'udp-sender'`
    [ -n "$pid" ] && killall udp-sender
}   

#
do_startdisk() {
    # target_image
    if [ -z "$target_image" ]; then
      # ask user to choose the target_image
      case "$task" in
        "save")
           target_image="ask_user"
	   ;;
        "restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             target_image="ask_user"
           else
             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
             # the return name will be only one image name.
             target_image="$(cat $ANS_TMP)"
             [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
             target_hd=$(cat $imagedir/$target_image/disk 2>/dev/null)
           fi
           ;;
        "multicast_restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             echo "You can not choose the image name in the client when using multicast restore! You must choose now."
           fi
           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
           # the return name will be only one image name.
           target_image="$(cat $ANS_TMP)"
           [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
           target_hd=$(cat $imagedir/$target_image/disk 2>/dev/null)
           ;;
         *)
           echo "Usage: $ocs_file startdisk {save|restore|multicast_restore} target_image target_hd"
           exit 0
           ;;
      esac
    fi

    # target_hd
    if [ -z "$target_hd" ]; then
      # ask user to choose the target_hd
      case "$task" in
        "save")
           target_hd="ask_user"
           ;;
        "restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             target_hd="ask_user"
           else
             target_hd=$(cat $imagedir/$target_image/disk)
           fi
           ;;
        "multicast_restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             echo "You can not input the target harddrive in the client when using multicast restore! You must choose now."
           fi
           target_hd=$(cat $imagedir/$target_image/disk)
           ;;
      esac
    fi

    # start
    if [ -z "$target_image" -o "$target_image" = "$imagedir/" -o -z "$target_hd" ]; then 
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "You didn't specify which file or disk to $task"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      exit 1 
    fi

    get_multicast_restore_mode_if_mcast

    #
    echo start_ocs_service $task"disk" "$target_image $target_hd" "$n_clients" 
    start_ocs_service $task"disk" "$target_image $target_hd" "$n_clients"
} # end of do_startdisk

#
do_startparts () {
    # target_image
    if [ -z "$target_image" ]; then
      # ask user to choose the target_image
      case "$task" in
        "save")
           target_image="ask_user"
	   ;;
        "restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             target_image="ask_user"
	   else
             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
             # the return name will be only one image name.
             target_image="$(cat $ANS_TMP)"
             [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
           fi
           ;;
        "multicast_restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             echo "You can not choose the image name in the client when using multicast restore! You must choose now."
           fi
           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
           # the return name will be only one image name.
           target_image="$(cat $ANS_TMP)"
           [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
           ;;
         *)
           echo "Usage: $ocs_file startparts {save|restore|multicast_restore} target_image target_parts"
           exit 0
           ;;
      esac
    fi

    # target_parts
    if [ -z "$target_parts" ]; then
      # ask user to choose the target_parts
      case "$task" in
        "save")
           target_parts="ask_user"
           ;;
        "restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             target_parts="ask_user"
           else
             ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
             trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
             get_existing_partitions $ANS_TMP $target_image
             # we have to remove " (comes with checklist in dialog) 
             # so that for loop will work (Specially for FC3/4...)
             target_parts="$(cat $ANS_TMP | tr -d \")"
             [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
           fi
           ;;
        "multicast_restore")
	   if [ "$select_img_in_client" = "yes" ]; then
             # if user want to select the image name in client
             echo "You can not choose the image name in the client when using multicast restore! You must choose now."
           fi
           ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
           trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
           get_existing_partitions $ANS_TMP $target_image
           # we have to remove " (comes with checklist in dialog) 
           # so that for loop will work (Specially for FC3/4...)
           target_parts="$(cat $ANS_TMP | tr -d \")"
           [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
           ;;
      esac
    fi

    #
    if [ -z "$target_image" -o "$target_image" = "$imagedir/" -o -z "$target_parts" ]; then 
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "You didn't specify which file or partitions to $task"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      exit 1 
    fi

    get_multicast_restore_mode_if_mcast

    #
    echo start_ocs_service $task"parts" "$target_image \"$target_parts\"" "$n_clients"
    start_ocs_service $task"parts" "$target_image \"$target_parts\"" "$n_clients"
} # end of do_startparts

#
prompt_to_turn_on_client() {
    local type=$1
    case "$type" in
      "save")
         turn_on_client_msg="$msg_turn_on_client_to_make_template"
         ;;
      "restore"|"multicast_restore")
         turn_on_client_msg="$msg_turn_on_client_to_clone"
    esac
    echo $msg_delimiter_star_line
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$turn_on_client_msg"
    echo "$msg_etherboot_5_4_is_required"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
} # end of prompt_to_turn_on_client
#
get_multicast_restore_mode_if_mcast() {
    # n_clients (for multicast only)
    if [ -z "$n_clients" -a "$task" = "multicast_restore" ]; then
      if [ -z "$mcast_wait_time" -a -z "$n_clients" -a -z "$mcast_max_wait_time" ] ; then
       ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
       trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
       ask_time_or_clients_to_wait $ANS_TMP
       . $ANS_TMP
       # we will get time_to_wait or clients_to_wait.
       [ -n "$time_to_wait" ] && mcast_wait_time="$time_to_wait"
       [ -n "$clients_to_wait" ] && n_clients="$clients_to_wait"
       [ -n "$max_time_to_wait" ] && mcast_max_wait_time="$max_time_to_wait"
       [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
      fi
    fi
} # end of get_multicast_restore_mode_if_mcast
#
add_runlevel_1_in_pxelinux_cfg_drbl_block() {
  lines=$(get_pxecfg_image_block drbl $PXE_CONF)
  begin_line=$(echo $lines | awk -F" " '{print $1}')
  end_line=$(echo $lines | awk -F" " '{print $2}')
  tag_found="$(head -n $end_line $PXE_CONF | tail -n $(($end_line-$begin_line)) | grep -Ei "^[[:space:]]*append[[:space:]]*.*[[:space:]]+\<1\>([[:space:]]+|$)")"
  if [ -z "$tag_found" ]; then
    sub_menu_label_cmd="if ($begin_line..$end_line) {s/(^[[:space:]]*append[[:space:]]+.*)/\$1 1/i}"
    perl -pi -e "$sub_menu_label_cmd" $PXE_CONF
  fi
} # end of add_runlevel_1_in_pxelinux_cfg_drbl_block()
#
remove_runlevel_1_in_pxelinux_cfg_drbl_block() {
  # remove 1 from the append .... like this in pxelinux config:
  # append initrd=initrd-pxe.img ramdisk_size=12288  devfs=nomount drblthincli=off selinux=0 1
  lines=$(get_pxecfg_image_block drbl $PXE_CONF)
  begin_line=$(echo $lines | awk -F" " '{print $1}')
  end_line=$(echo $lines | awk -F" " '{print $2}')
  sub_menu_label_cmd="if ($begin_line..$end_line) {s/(^[[:space:]]*append[[:space:]]+.*)[[:space:]]+1([[:space:]]+.*)/\$1\$2/i}"
  perl -pi -e "$sub_menu_label_cmd" $PXE_CONF
} # end of remove_runlevel_1_in_pxelinux_cfg_drbl_block()

##############
#### main ####
##############

# HOST_OPTION_MODIFY: add this part for specifying the hosts.
if [ "$LIST_HOST" = "on" ]; then
  # copy the skeleton file
  PXE_CONF="$PXELINUX_DIR/default_skeleton"
  cp -f $PXELINUX_DIR/default $PXE_CONF
else
  PXE_CONF="$PXELINUX_DIR/default"
  # Clean all the previous saved config file.
  # These files maybe look like: 01-MAC address, something like C0A8XXXX (only for 192.168.x), default_skeleton
  echo -n "Clean all the previous saved config file if they exist..."
  for ifile in $PXELINUX_DIR/*; do
    [ "$ifile" != "$PXE_CONF" -a -f "$ifile" ] && rm -f $ifile
  done
  echo "done!"
fi

# ocs root
mk_ocsroot=0
if [ ! -e "$ocsroot" ]; then
  mkdir -p $ocsroot
  [ $? -eq 0 ] && mk_ocsroot=1
fi
if [ ! -d "$ocsroot" ]; then
  echo "Please make sure your $ocsroot is a directory!!!"
  echo "We can NOT go on! Press \"c\" to enter command prompt or any other key to quit the program..." 
  read fail_answer
  case "$fail_answer" in
    [cC]) sulogin ;;
       *) exit 1 ;;
  esac
fi

# parse postrun as command
[ -z "$postrun" ] && postrun="$POSTRUN_DEF"
postrun="$(convert_post_run_cmd "$postrun")"

#
case "$1" in
  "startdisk")
    task=$2
    target_image=$3
    target_hd=$4
    do_startdisk
    copy_rc1d_for_drbl_ssi
    prompt_to_turn_on_client $task
    ;;
  "startparts")
    task=$2
    target_image=$3
    target_parts=$4
    do_startparts
    copy_rc1d_for_drbl_ssi
    prompt_to_turn_on_client $task
    ;;
  "stop")
    remove_rc1d_for_drbl_ssi
    stop_ocs_service yes
    # Make client to boot in drbl mode with menu label specified.
    # get the distribution name and type: OS_Version and OS_type
    check_distribution_name
    force_pxe_clients_boot_drbl "$FULL_OS_Version $powerful_client_menu_label"
    # remove runlevel 1 to kernel parameter in pxelinux config
    remove_runlevel_1_in_pxelinux_cfg_drbl_block

    # If the mode is in always_restore, we will set local boot as default. Since
    # the always_restore mode is only useful with local OS exists.
    if [ "$always_restore" = "yes" ]; then
      force_pxe_clients_boot_local
    fi
    ;;
  "savedisk")
    task_preprocessing $1
    task_savedisk $2 $3
    task_notify_localboot
    task_postprocessing
    ;;
  "restoredisk")
    task_preprocessing $1
    task_restoredisk $2 $3
    task_notify_localboot
    task_postprocessing
    ;;
  "saveparts")
    task_preprocessing $1
    task_saveparts "$2" "$3"
    task_notify_localboot
    task_postprocessing
    ;;
  "restoreparts")
    task_preprocessing $1
    task_restoreparts "$2" "$3"
    task_notify_localboot
    task_postprocessing
    ;;
  "multicast_restoredisk")
    task_preprocessing $1
    task_multicast_restoredisk $2 $3 $4
    task_notify_localboot
    task_postprocessing
    ;;
  "multicast_restoreparts")
    task_preprocessing $1
    task_multicast_restoreparts $2 "$3" "$4"
    task_notify_localboot
    task_postprocessing
    ;;
  *)
    USAGE
    ;;
esac

exit 0
