# -*-Shell-script-*-
# Steven Shiau <steven _at_ nchc org tw>
# License: GPL
#
# functions	This file contains functions to be used by most or all
#		shell scripts in the DRBL environment
#
# cut the functions from drblsrv-rh, drblsrv-mdk, drbl.pl
# /opt/drbl/sbin/drbl-functions
# drbl-client-root-passwd 
# a lot of functions in drbl-login-switch ?
# mknic-nbi create_depmod_env modify
# resize_part check_input_hd()

# Do NOT set the locale by "export LC_ALL=C". It will make dialog distortation.

# Source DRBL setting
. /opt/drbl/conf/drbl.conf

# Append a default search path.
PATH="$DRBL_SCRIPT_PATH/sbin:$DRBL_SCRIPT_PATH/bin:$PATH:/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"
export PATH

# setup some parameters for color output. The variables should be already 
# defined in the /etc/init.d/functions in RH-like distribution 

[ -z "$SETCOLOR_SUCCESS" ] && SETCOLOR_SUCCESS="echo -en \\033[1;32m"
[ -z "$SETCOLOR_FAILURE" ] && SETCOLOR_FAILURE="echo -en \\033[1;31m"
[ -z "$SETCOLOR_WARNING" ] && SETCOLOR_WARNING="echo -en \\033[1;33m"
[ -z "$SETCOLOR_NORMAL"  ] && SETCOLOR_NORMAL="echo -en \\033[0;39m"

# some functions
# get OS type
get_os_type() {
  case "$OS_Version" in
     RH*|FC*|CO*)
        OS_type="RH"
        ;;
     MD[KV]*)
        OS_type="MDK"
        ;;
     DBN*)
        OS_type="DBN"
        ;;
     SUSE*)
        OS_type="SUSE"
        ;;
     *)
        echo "This version in this distribution is NOT supported by DRBL, maybe you can try to use the drbl in testing or unstable repository. Program terminated!"
        exit 1 
  esac
}

#
check_distribution_name() {
  local VER
  local DIST
  local FULL_DIST
  # Reset these values
  OS_Version=
  FULL_OS_Version=
  # FULL_OS_Version is only used in the label
  # like the files /tftpboot/nbi_img/pxelinux.cfg/* (default or 0A....).
  # OS_Version is used in drblsrv and drblpush to judge the dist and version.
  if [ -f /etc/fedora-release ]; then
   VER=$(rpm -qf /etc/fedora-release --qf "%{VERSION}" | sed "s/[^0-9\.]//g")
   DIST=FC
   FULL_DIST=Fedora
  elif [ -f /etc/mandriva-release ]; then
   VER=$(rpm -qf /etc/mandriva-release --qf "%{VERSION}" | sed "s/[^0-9\.]//g")
   DIST=MDV
   FULL_DIST=Mandriva
  elif [ -f /etc/mandrake-release ]; then
   # /etc/mandrake-release must be after the /etc/mandriva-release, since the
   # new Mandriva distributions has /etc/mandrake-release, too.
   VER=$(rpm -qf /etc/mandrake-release --qf "%{VERSION}" | sed "s/[^0-9\.]//g")
   if [ "$VER" = "10.2" ]; then
     # Exception:
     # "Mandrakelinux release 10.2.*" /etc/mandrakelinux-release
     # OS_Version="MDV2005"
     DIST=MDV
     FULL_DIST=Mandriva
     VER=2005
   else
     DIST=MDK
     FULL_DIST=Mandrake
   fi
  elif [ -e /etc/SuSE-release ]; then
   VER=$(rpm -qf /etc/SuSE-release --qf "%{VERSION}" | sed "s/[^0-9\.]//g")
   DIST=SUSE
   FULL_DIST=SuSE
  elif [ -f /etc/redhat-release ]; then
   # /etc/redhat-release must be the last "elif" for RH-like dist, since there
   # are so many distributions are based on RH, and do exist /etc/redhat-release
   if grep -q -i "CentOS" /etc/redhat-release; then
     # The release of CentOS is a little difference, we can not use
     # rpm -qf /etc/redhat-release --qf "%{VERSION}" to get the version, since
     # we will get "4", but actually it's "4.2" or "4.1"
     VER=$(cat /etc/redhat-release | sed "s/[^0-9\.]//g")
     DIST=CO
     FULL_DIST=CentOS
   elif grep -q -i "Scientific Linux" /etc/redhat-release; then
     # Scientific Linux
     # To simplify that, we only support drblsrv-offline for Scientific Linux
     # The DIST is set as CO for compatibility.
     VER=$(cat /etc/redhat-release | sed "s/[^0-9\.]//g")
     DIST=CO
     FULL_DIST=SL
   elif grep -q -i "OSSII" /etc/redhat-release; then
     # The release of OSSII M6 Linux is like:
     # OSSII M6 Linux
     # To simplify that, we only support drblsrv-offline for OSSII
     # The DIST is set as CO for compatibility, basically it's useless here.
     VER=$(grep -Eo "M[[:digit:]]" /etc/redhat-release)
     DIST=CO
     FULL_DIST=OSSII
   else
     # The last one... RedHat
     # In some case, no idea why, when run this as root in RH9, it will crash
     # [root]# rpm -qf --qf '%{VERSION}' /etc/redhat-release
     # Segmentation fault
     # So we do not use this:
     #VER=$(rpm -qf /etc/redhat-release --qf "%{VERSION}" | sed "s/[^0-9\.]//g")
     # We use this:
     VER=$(cat /etc/redhat-release | sed "s/[^0-9\.]//g")
     DIST=RH
     FULL_DIST=RedHat
   fi
  elif [ -f /etc/debian_version ]; then
   DIST=DBN
   FULL_DIST=Debian
   # get the version
   if grep -qE "(testing/unstable|sid)" /etc/debian_version; then
     VER="-TU"
     # Here we assign a better tag for FULL_OS_Version
     FULL_OS_Version="Debian Testing-Unstable"
   else
     VER="$(cat /etc/debian_version)"
   fi
   # Overwrite the FULL_OS_Version if others are found. Especially for Ubuntu
   if [ -e /etc/lsb-release ]; then
     . /etc/lsb-release
     FULL_OS_Version="$DISTRIB_ID $DISTRIB_RELEASE"
   fi
  fi

  if [ -z "$DIST" -o -z "$VER" ]; then
    echo "The distribution or version is unknown! Program terminated!"
    exit 1
  fi
  # If not assigned, assign it
  [ -z "$OS_Version" ] && OS_Version=${DIST}${VER}
  [ -z "$FULL_OS_Version" ] && FULL_OS_Version="${FULL_DIST} ${VER}"

  # get the OS_type variable
  get_os_type
  #
  # echo $OS_Version
}

# create the shared library program runtime env.
# ldd might give these 3 type results:
# 1. newer version (FC3):
# ldd /sbin/depmod
#         libc.so.6 => /lib/libc.so.6 (0x00a08000)
#         /lib/ld-linux.so.2 (0x009f1000)
# 2. older version (RH8/9,FC1/2):
# ldd /sbin/depmod
#         libc.so.6 => /lib/libc.so.6 (0x40025000)
#         /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
# 3. MDK10
#         linux-gate.so.1 =>  (0xffffe000)
#         libc.so.6 => /lib/tls/libc.so.6 (0x40028000)
#         /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
#RH9:
#libc.so.6 => /lib/libc.so.6 (0x40025000)
#/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
#FC1:
#libc.so.6 => /lib/libc.so.6 (0x00a35000)
#/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00a20000)
#FC2:
#libc.so.6 => /lib/libc.so.6 (0x00b76000)
#linux.so.2 => /lib/ld-linux.so.2 (0x00b61000)
#FC3:
#libc.so.6 => /lib/tls/i686/libc.so.6 (0x007b3000)
#linux.so.2 (0x0079c000)
#mdk10:
#linux-gate.so.1 =>  (0xffffe000)
#libc.so.6 => /lib/tls/libc.so.6 (0x40028000)
#/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
 
create_depmod_env() {
  # This function is useless from drbl 1.7.6-23 or later, since we use 
  # depmod -b baseroot/ instead of chroot.
  # However, we still keep this for ref.
  local newroot="$1"
  [ -z $newroot ] && exit 1
  # some distribution, like Mandriva, use link in /sbin/depmod, so we clean it
  # to avoid some error.
  [ -e "$newroot/sbin/depmod" ] && rm -f $newroot/sbin/depmod
  # -L: always follow symbolic links
  cp -L --parents /sbin/depmod* $newroot
  if ldd /sbin/depmod &>/dev/null; then
    depmod_lib_need=$(ldd /sbin/depmod | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
    for imod in $depmod_lib_need; do
        cp --parents $imod $newroot
    done
  fi
}
clean_depmod_env() {
  # This function is useless from drbl 1.7.6-23 or later, since we use 
  # depmod -b baseroot/ instead of chroot.
  # However, we still keep this for ref.
  local newroot="$1"
  [ -z $newroot ] && exit 1
  [ -f $newroot/sbin/depmod ] && rm -f $newroot/sbin/depmod
  # for MDK 9.2, there is another depmod.old...
  [ -f $newroot/sbin/depmod.old ] && rm -f $newroot/sbin/depmod.old
  if ldd /sbin/depmod &>/dev/null; then
    depmod_lib_need=$(ldd /sbin/depmod | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
    for f2rm in $depmod_lib_need; do
      [ -f "$newroot/$f2rm" ] && rm -f $newroot/$f2rm
    done
  fi
}

# function to create chkconfig environment
create_chkconfig_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
  cp --parents /sbin/chkconfig $newroot
  if ldd /sbin/chkconfig &>/dev/null; then
    chkconfig_lib_need=$(ldd /sbin/chkconfig | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
    for imod in $chkconfig_lib_need; do
      cp --parents $imod $newroot
    done
  fi
}

# function to clean chkconfig environment
clean_chkconfig_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
  chkconfig_lib_need=$(ldd /sbin/chkconfig | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
  [ -f $newroot/sbin/chkconfig ] && rm -f $newroot/sbin/chkconfig
  for f2rm in $chkconfig_lib_need; do
    [ -f $newroot/$f2rm ] && rm -f $newroot/$f2rm
  done
}

# function to create insserv environment
create_insserv_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
  cp --parents /sbin/insserv $newroot
  if ldd /sbin/insserv &>/dev/null; then
    insserv_lib_need=$(ldd /sbin/insserv | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
    for imod in $insserv_lib_need; do
      cp --parents $imod $newroot
    done
  fi
}
# Check if root or not
check_if_root() {
   if [ ! "$UID" = "0" ]; then
     echo
     echo "[$LOGNAME] You need to run this script \"`basename $0`\" as root."
     echo
     exit 1
   fi
}

# function to get the autologin_account
# input is the host dir, like /tftpboot/nodes/192.168.1.1, then
# return the auto_login ID
get_autologin_account() {
    local ihost="$1"
    local ip HOSTNAME
    # $auto_login_id and $echo_no_digit_0_1 are global variables
    # auto login username is set as hostname, i.e. use client's hostname as the auto-login ID.
    # Reset variable auto_login_id
    auto_login_id=""
    if [ -e /etc/debian_version ]; then
      # Debian
      auto_login_id="$(cat $ihost/etc/hostname 2>/dev/null)"
    elif [ -e /etc/SuSE-release ]; then
      # SuSE
      auto_login_id="$(cat $ihost/etc/HOSTNAME 2>/dev/null | sed -e "s/\..*//g")"
    else
      # RH-like
      if [ -e $ihost/$SYSCONF_PATH/network ]; then
        HOSTNAME=""
        . $ihost/$SYSCONF_PATH/network
        auto_login_id="$HOSTNAME"
      fi
    fi
    # if no hostname, such as in DRBL SSI mode, try to map the hostname from $IP_HOST_TABLE
    if [ -z "$auto_login_id" ]; then
      ip="$(basename $ihost)"
      auto_login_id="$(awk -F" " "/^$ip[[:space:]]+/ {print \$2}" $IP_HOST_TABLE)"
    fi
}
# only show existing autologin account
get_existing_autologin_account() {
    local ihost="$1"
    get_autologin_account $ihost
    if grep -q $auto_login_id /etc/passwd ; then
      # account $auto_login_id exists, show it
      echo "$auto_login_id"
    fi
}

# check switch if it is input correct as on or off
check_switch_on_off() {
  local switch="$1"
  case "$switch" in
    on|ON|[oO]|[nN]|off|OFF|[oO][fF][fF])
         true;;
     "")
         usage && exit 1
         ;;
     *)
         echo "You must specify \"on\" or \"off\" !!! Program stop!!!"
         exit 1
         ;;
  esac
}

#
create_authconfig_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
   file_need="/usr/sbin/authconfig /usr/sbin/pwconv /usr/sbin/grpconv"
   cp --parents $file_need $newroot

   if ldd /usr/sbin/authconfig &>/dev/null; then
     authconfig_lib_need=$(ldd /usr/sbin/authconfig | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
     for imod in $authconfig_lib_need; do
       cp --parents $imod $newroot
     done
   fi
   if ldd /usr/sbin/pwconf &>/dev/null; then
     pwconf_lib_need=$(ldd /usr/sbin/pwconf | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
     for imod in $pwconf_lib_need; do
       cp --parents $imod $newroot
     done
   fi
   if ldd /usr/sbin/grpconv &>/dev/null; then
     grpconv_lib_need=$(ldd /usr/sbin/grpconv | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
     for imod in $grpconv_lib_need; do
       cp --parents $imod $newroot
     done
   fi
}

#
create_chpasswd_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
  # /lib* includes /lib and /lib64
  PAM_need_files="/lib*/security/ /usr/lib*/libcrack*.so* /usr/lib*/cracklib_dict.* /lib*/libnss_files*.so* /lib*/libnsl*.so* /usr/lib/pwdutils/*.so*"
  chpasswd_lib_need=$(ldd /usr/sbin/chpasswd | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
  echo_lib_need=$(ldd /bin/echo | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
  for imod in $chpasswd_lib_need $echo_lib_need; do
    cp --parents $imod $newroot
  done
  # /bin/echo is necessary for chpasswd
  cp --parents /bin/echo $newroot
  cp --parents /usr/sbin/chpasswd $newroot
  #cp --parents -r $PAM_need_files $newroot
  for ipam in $PAM_need_files; do
    [ -n "$(ls $ipam 2>/dev/null)" ] && cp --parents -r $ipam $newroot
  done
  # we need sh to run the script, borrow it from mkpxeinitrd-net's busybox
  cp /usr/lib/mkpxeinitrd-net/initrd-skel/bin/sh $newroot/bin
}

#
#create_passwd_env() {
#  local newroot="$1"
#  [ -z $newroot ] && exit 1
#  # /lib* includes /lib and /lib64
#  PAM_need_files="/lib*/security/ /usr/lib*/libcrack*.so* /usr/lib*/cracklib_dict.* /lib*/libnss_files*.so* /lib*/libnsl*.so* /usr/lib/pwdutils/*.so*"
#  passwd_lib_need=$(ldd /usr/bin/passwd | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
#  for imod in $passwd_lib_need; do
#    cp --parents $imod $newroot
#  done
#  cp --parents /usr/bin/passwd $newroot
#
#  # /bin/echo is necessary for passwd --stdin
#  cp --parents /bin/echo $newroot
#
#  cp --parents -r $PAM_need_files $newroot
#  # we need sh to run the script, borrow it from mkpxeinitrd-net's busybox
#  cp /usr/lib/mkpxeinitrd-net/initrd-skel/bin/sh $newroot/bin
#}

clean_passwd_env() {
  local newroot="$1"
  [ -z "$newroot" ] && exit 1
#  passwd_lib_need=$(ldd /usr/bin/passwd | cut -d" " -f3)
#  [ -f "$newroot/usr/bin/passwd" ] && rm -f $newroot/usr/bin/passwd
#  [ -f "$newroot/bin/echo" ] && rm -f $newroot/bin/echo
#  for f2rm in $passwd_lib_need; do
#    [ -f $newroot/$f2rm ] && rm -f $newroot/$f2rm
#  done
#  for f2rm in $PAM_need_files; do
#    [ -f $newroot/$f2rm ] && rm -f $newroot/$f2rm
#  done
#  [ -f "$newroot/bin/sh" ] && rm -f $newroot/bin/sh
  for d2rm in bin lib usr; do
    if [ -d "$newroot/$d2rm" ]; then
      [ -n "$verbose" ] && echo "Cleaning the unnecessary directory $newroot/$d2rm ..."
      rm -rf $newroot/$d2rm
    fi
  done
}

clean_chpasswd_env() {
  local newroot="$1"
  [ -z "$newroot" ] && exit 1
  for d2rm in bin lib usr; do
    if [ -d "$newroot/$d2rm" ]; then
      [ -n "$verbose" ] && echo "Cleaning the unnecessary directory $newroot/$d2rm ..."
      rm -rf $newroot/$d2rm
    fi
  done
}

# check if the user input /dev/hda, /dev/hdb...
check_input_hd() {
    local target_hd="$1"
    case "$target_hd" in
	 [hs]d[a-z])
	   continue
	   ;;
	 *)
	  echo "Unknown HD device! Program stop!"
          Usage
	  exit 1
    esac
}

## description:
##   convert devfs names into normal short ones, written by Tom Rini.
fixdevfs() {
    ## get partition number, if any
    local PARTNUM="${1##*[a-z]}"
    ## Find the bus type.
    local TYPE="$(v=${1#/dev/} ; echo ${v%/host*})"
    ## Find the host number.
    local HOST="$(v=${1#/dev/*/host} ; echo ${v%/bus*})"
    ## Find the bus number.
    local BUS="$(v=${1#/dev/*/bus} ; echo ${v%/tar*})"
    ## Find the target.
    local TARGET="$(v=${1#/dev/*/target} ; echo ${v%/lun*})"

    case "$TYPE" in
	ide)
	case "$HOST" in
	    0)
	    case "$TARGET" in
		0)
		local DEV=hda
		;;
		1)
		local DEV=hdb
		;;
	    esac
	    ;;
	    1)
	    case "$TARGET" in
		0)
	        local DEV=hdc
	        ;;
		1)
		local DEV=hdd
		;;
	    esac
	    ;;
	    2)
	    case "$TARGET" in
		0)
	        local DEV=hde
	        ;;
		1)
		local DEV=hdf
		;;
	    esac
	    ;;
	    3)
	    case "$TARGET" in
		0)
	        local DEV=hdg
	        ;;
		1)
		local DEV=hdh
		;;
	    esac
	    ;;
	    *)
		echo "${1#/dev/}"
		#echo "Unable to translate this device, try again without devfs."
		return 1
	esac
	local DEV="${DEV}${PARTNUM}"
	echo "$DEV"
	return 0
	;;
	scsi)
	local LUN="$(v=${1#/dev/*/lun} ; echo ${v%/*})"

	## In this case, we need to figure out what number our device is
	local DEVCOUNT=0

	## copy scsi file into a variable removing "Attached Devices"
	## which is the first line. this avoids a lot of
	## [incmopatible] crap later, and improves readability.

	## find number of lines once and recycle that number, to save
	## some time (linecount is a bit slow). subtract one line
	## to scrap Attached Devices:

	local SCSILINES="$(($(wc -l /proc/scsi/scsi | cut -d' ' -f1) - 1))"
	local PROCSCSI="$(cat /proc/scsi/scsi | tail -n $SCSILINES)"

	for i in $(seq $(($SCSILINES / 3))) ; do

	    ## put every scsi device into one single line
	    local DEVINFO="$(echo "$PROCSCSI" | head -n $(($i * 3)) | tail -n 3)"
	    [ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVINFO=$DEVINFO"

	    ## cut the type field, expect "Direct-Access" later.
	    local DEVTYPE="$(v=$(echo ${DEVINFO##*Type: }) ; echo ${v%% *})"
	    [ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVTYPE=$DEVTYPE"

	    if [ "$DEVTYPE" = "Direct-Access" ] ; then
		## Lets find out some more information
		## get the device id.
		local DEVID="$(v=$(echo ${DEVINFO##*Id: }) ; n=$(echo ${v%% *}) ; echo ${n#*0})"
		[ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVID=$DEVID"

		## get the device lun.
		local DEVLUN="$(v=$(echo ${DEVINFO##*Lun: }) ; n=$(echo ${v%% *}) ; echo ${n#*0})"
		[ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVLUN=$DEVLUN"

		## get the device channel.
		local DEVCHAN="$(v=$(echo ${DEVINFO##*Channel: }) ; n=$(echo ${v%% *}) ; echo ${n#*0})"
		[ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVCHAN=$DEVCHAN"

		## get the scsi host id.
		local DEVHOST="$(v=$(echo ${DEVINFO##*Host: scsi}) ; echo ${v%% *})"
		[ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVHOST=$DEVHOST"

		local DEVCOUNT="$(($DEVCOUNT + 1))"
		[ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: fixdevfs: DEVCOUNT=$DEVCOUNT"
		if [ "$DEVHOST" = "$HOST" -a "$DEVCHAN" = "$BUS" -a \
		    "$DEVID" = "$TARGET" -a "$DEVLUN" = "$LUN" ] ; then
		    local DEV="sd$(smalltr $DEVCOUNT)${PARTNUM}"
		    echo "$DEV"
		    return 0
		fi
	    fi
	done
	echo "${1#/dev/}"
	#echo "Unable to translate this device, try again without devfs."
	return 1
	;;
	*)
	echo "${1#/dev/}"
	#echo "Unknown bus"
	return 1
	;;
    esac
    ## we should never get here
    return 1
}

conv_devfspart_to_tradpart() {
   # function to convert devfs partitions to traditional partitions
   # fix the disk/partition, we do not want the devfs style for clonezilla
   TARGET_FILE="$1"
   [ -z "$TARGET_FILE" ] && echo "No output filename! Program stop!!!" && return 1
   [ -f "$TARGET_FILE" ] && rm -f $TARGET_FILE
   while read major minor block p x; do
     p="$(fixdevfs "/dev/$p")"
     echo "$major $minor $block $p" >> $TARGET_FILE
   done < /proc/partitions
}

# get dhcpd interface
get_dhcpd_interface() {
  local INTERFACES= INTERFACES_TMP=
  if [ -e /etc/debian_version ]; then
    # Debian
    . $SYSCONF_PATH/$DHCP_SRV_NAME
  elif [ -e /etc/SuSE-release ]; then
    # SuSE
    . $SYSCONF_PATH/$DHCP_SRV_NAME
    INTERFACES="$DHCPD_INTERFACE"
  else
    # RH-like
    # get the setting of dhcp server
    . $SYSCONF_PATH/$DHCP_SRV_NAME
    # collect those interface, filter out other options, such as -p 60
    INTERFACES=""
    for ieth in $DHCPDARGS; do
       INTERFACES_TMP="$(echo $ieth |grep -o eth.*)"
       INTERFACES="$INTERFACES $INTERFACES_TMP"
    done
  fi
  echo $INTERFACES
}

# HD dma status
check_hd_dma() {
   # input example: /dev/hda
   # return 0/1, 0: DMA off, 1: DMA on
   local hd_dev=$1
   DMA_status=$(/sbin/hdparm -d $hd_dev |grep using_dma |awk  '{print $3}')
   echo "$DMA_status"
}

# turn on hd dma
turn_on_hd_dma() {
    local hd_dev="$1"
    echo "*******************************************"
    echo "Try to turn on the harddisk \"$hd_dev\" DMA..."
    if [ -n "$hd_dev" ]; then
      if [ "$(check_hd_dma $hd_dev)" = "0" ]; then
        # turn on DMA if it's off (0)
        /sbin/hdparm -d1m1c1 $hd_dev
      fi
      # check the result
      DMA=`check_hd_dma $hd_dev`
      if [ -n "$DMA" ]; then
         # This is IDE device, we can get DMA status
         [ "$DMA" -eq 0 ] && echo "Warning!!! Failed to turn on harddisk \"$hd_dev\" DMA... The clone performance might be NOT good..." && sleep 5
      else
         # This is SCSI device, we can not get DMA status
         echo "No HD DMA information, maybe this not a IDE device!"
      fi
    else
         echo "To turn on HD dma, you have to specify the HD dev!"
    fi
    echo "*******************************************"
}

# do not let screen be blank
screen_not_blank() {
    /usr/bin/setterm -blank 0
}

# set passwd login for single user mode
set_clients_rc1_passwd() {
  local rc1_sulogin="~~:S:wait:/sbin/sulogin"
  IP_LIST=$*
  # make a flag "LIST_HOST"
  [ -n "$IP_LIST" ] && LIST_HOST="on"

  for ihost in $drblroot/*; do
     # skip those IP not listed in the $IP_LIST
     if [ "$LIST_HOST" = "on" ]; then
       [ -z "$(echo $IP_LIST | grep ${ihost##/*/})" ] && continue
     fi

    if ! grep -qE ^$rc1_sulogin $ihost/etc/inittab 2>/dev/null; then
      echo "Set the single user password for client ${ihost##/*/}, this will be safer..."
      cat <<-EOF >> $ihost/etc/inittab

# Single User Mode Password, added by DRBL
$rc1_sulogin
EOF
    fi
  done
}

# function to active the /proc/partitions. For SCSI devices, even when device
# modules is loaded, the device is not shown in /proc/partitions if no program
# like fdisk or sg_map runs... We also do it for /dev/hdx.
active_proc_partitions() {
   echo -n "Activating the partition info in /proc... "
   for ihd in /dev/[hs]d[a-z]; do
      fdisk -l $ihd &> /dev/null
   done
   echo "done!"
}
#
get_lang_index() {
   local lang=$1
   local language
   case "$lang" in
      "1")
           language="tw.BIG5"
           ;;
      "2")
           language="tw.UTF-8"
           ;;
       *)
           language="en"
           ;;
   esac
   echo $language
}

check_kernel_nfsd_tcp_config() {
  # note the kernel should be the one is using in the drbl server
  kernel_ver=$(uname -r)
  # check if /boot/config-$kernel_ver exists
  # kernel config is either in /boot/ 
  kernel_config="/boot/config-$kernel_ver"
  echo "Checking server kernel config \"$kernel_config...\""
  if [ ! -f $kernel_config ]; then 
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$kernel_config does NOT exist!"
    echo "I can not judge whether NFS over TCP is supported in the kernel you are using!!!"
    echo "We will assume that the NFS server use UDP protocol!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    protocol="udp"
  else
    # we assume the the priority is higher for EXT2, i.e. EXT2 option will overwrite the CRAMFS.
    if [ -n "$(grep "^CONFIG_NFSD_TCP=y" $kernel_config)" ]; then
      protocol="tcp"
    else
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "$kernel_config is found but NFS over TCP is not supported in the kernel you are using for the DRBL clients!!!"
      echo "We will use NFS over UDP !"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      protocol="udp"
    fi
  fi
  case "$protocol" in
    [tT][cC][pP])
       rc=0 
       ;;
    [uU][dD][pP])
       rc=1 
       ;;
  esac
  return $rc
}

switch_clients_init() {
   # input: client_init IP_LIST
   # if IP_LIST is none, this function will process all the clients.
   local CLIENT_INIT
   local IP_LIST
   CLIENT_INIT=$1
   shift
   IP_LIST=$*
   # make a flag "LIST_HOST"
   [ -n "$IP_LIST" ] && LIST_HOST="on"
   [ -z "$CLIENT_INIT" ] && echo "No specified mode for client inittab!!! Program stop!!!" && exit 1
   for host in $drblroot/*; do
     # skip those IP not listed in the $IP_LIST
     if [ "$LIST_HOST" = "on" ]; then
       [ -z "$(echo $IP_LIST | grep ${host##/*/})" ] && continue
     fi

     echo "Setting the graphic mode for node IP = $(basename $host)..."
     # set the init to 5, i.e. default is X
     /usr/bin/perl -p -i -e "s/^id:[1-5]:initdefault:/id:$CLIENT_INIT:initdefault:/g" $host/etc/inittab
   done
}

# This "NEWER" program is borrowed from http://staff.washington.edu/corey/new-patches.cgi
# The original author:
# Corey Satten, corey @ cac.washington.edu, 06/26/03, release 1.8
# For the latest version visit: http://staff.washington.edu/corey/tools.html
#
# Modified by Blake Huang and Steven Shiau to use in DRBL
NEWER='
    # Function newer:
    # Return 1 if second arg is "newer" than the first arg, else return 0.
    #
    # Because extra dotted fields before a hyphen are more significant
    # than those after a hyphen, first split on hyphens, then loop over
    # dotted fields passing the hard alphanumerics to function "compare"
    # for further splitting and comparing.
    #
    # eg.  older bind-utils-8.2.2_P5-9     and  older gzip-1.2.4-14
    #      newer bind-utils-8.2.2_P7-0.6.2 and  newer gzip-1.2.4a-2
    #
    #      older apmd-3.0beta9-3           and  older rmt-0.4b4-11 
    #      newer apmd-3.0final-2           and  newer rmt-0.4b19-5.6x

    function newer(a, b,    na1, nb1, na, nb, minn, i, j) {
	#printf("newer called with %s %s\n", a, b)
	if ('"${EFLAG-0}"') return a!=b
	if (O) {na=a; a=b; b=na}
	na1 = split(a, A1, /-/)
	nb1 = split(b, B1, /-/)
	if (na1 != nb1) {
	  #printf "unsure about %s and %s\n", a, b > "/dev/stderr"
	  return 1 }
	for (j=1; j<=na1; ++j) {
	  na = split(A1[j], A, /\./)
	  nb = split(B1[j], B, /\./)
	  minn = na < nb ? na : nb
	  for (i=1; i<=minn; ++i) {
	    if ('"${DEBUG-0}"') \
	      printf(" newer%d comparing %s %s\n", i, A[i], B[i])>"/dev/stderr"
	    if ((A[i] B[i]) ~ /^[0-9]+$/) {
	      if (A[i]+0 < B[i]+0) return 1
	      if (A[i]+0 > B[i]+0) return 0 }
	    else if (A[i] "" != B[i] "") return compare(A[i], B[i])
	    }
	  if (nb > na) return 1
	  if (nb < na) return 0
	  }
	return 0
	}

    # Function compare (called only by function newer):
    # Return 1 if second arg is "newer" than the first arg, else return 0.
    #
    # This is harder than it looks: consider "v9" vs "v10a", etc.
    # split out and compare alternating fields of numeric and non-numeric

    function compare (a, b,    xa, xb) {
	#printf(" compare called with %s %s\n", a, b)
	while (length(a) && length(b)) {
	  if (a ~ /^[0-9]/) {
	    match(a, /^[0-9]+/)
	    xa = substr(a, 1, RLENGTH); a = substr(a, RLENGTH+1) }
	  else {
	    match(a, /^[^0-9]+/)
	    xa = substr(a, 1, RLENGTH); a = substr(a, RLENGTH+1) }
	  if (b ~ /^[0-9]/) {
	    match(b, /^[0-9]+/)
	    xb = substr(b, 1, RLENGTH); b = substr(b, RLENGTH+1) }
	  else {
	    match(b, /^[^0-9]+/)
	    xb = substr(b, 1, RLENGTH); b = substr(b, RLENGTH+1) }
	    #printf("  compare2 %s %s <%s> <%s>\n", xa, xb, a, b)
	  if ( (xa xb) ~ /^[0-9]+$/) {
	    if (xa+0 < xb+0) return 1
	    if (xa+0 > xb+0) return 0 }
	  else {
	    if (xa "" < xb "") return 1
	    if (xa "" > xb "") return 0 }
	  }
	if (length(b)) return 1
	else return 0
	}
    '
#
set_specific_host_pxe_conf() {
  local HOSTS="$*"
  local pxecfg pxecfg_MAC pxecfg_MIP OCS_TMP IP
  # 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
  parse_dhcpd_conf $OCS_TMP

  for ih in $HOSTS; do
    case "$ih" in
      *.*.*.*)
        # list by IP
	# the pxecfg will like "C0A80001"
	pxecfg="$($DRBL_SCRIPT_PATH/bin/gethostip.pl $ih)"
        # These files look like: 01-MAC address (with ":" -> "-"),
	# We'd better to clean the 01-MAC file if it exists since maybe it's created by ocsmgrd when received the results from client in the previous save/restoration. Since 01-MAC has higher priority than "C0A80001" style file in pxelinux, this is a must if we can find it, we have to remove it.
	# TODO: What if no MAC address setting in dhcpd.conf ? Then $OCS_TMP will be empty, then... ?
        pxecfg_MAC="01-$(grep ${ih} $OCS_TMP | awk -F" " '{print $3}' | tr ":" "-")"
        [ -f "$PXELINUX_DIR/$pxecfg_MAC" ] && rm -f $PXELINUX_DIR/$pxecfg_MAC
        ;;
      *:*:*:*:*:*)
        # list by MAC
	# for an Ethernet (ARP type 1) with address 88:99:AA:BB:CC:DD it would search for the filename 01-88-99-aa-bb-cc-dd (lowercase)
	pxecfg="$(echo $ih | tr "[A-Z]" "[a-z]" | tr ":" "-")"
	# append "01-" in the beginning
	pxecfg="01-$pxecfg"
	# We'd better to clean the IP-based setting file if it exists since maybe it's created by ocsmgrd when received the results from client in the previous save/restoration. Although 01-MAC has higher priority than "C0A80001" style file in pxelinux, this NOT a must if we can find it, but we remove it to avoid confusion.
        IP="$(grep -E ${ih} $OCS_TMP | awk -F" " '{print $2}')"
	pxecfg_MIP="$($DRBL_SCRIPT_PATH/bin/gethostip.pl $IP)"
        [ -f "$PXELINUX_DIR/$pxecfg_MIP" ] && rm -f $PXELINUX_DIR/$pxecfg_MIP
        ;;
    esac
    echo -n "Generate the PXE config file for host $ih ... "
    cp -f $PXELINUX_DIR/default_skeleton $PXELINUX_DIR/$pxecfg
    echo "done!"
  done
  [ -f "$OCS_TMP" ] && rm -f $OCS_TMP
} # end of set_specific_host_pxe_conf

# function to get the pxecfg image block line number
get_pxecfg_image_block() {
     local IMG_NAME="$1"
     local PXE_CONF_TMP="$2"
     [ -z "$IMG_NAME" ] && exit 1
     # By using
     # grep -Ei -n '^[[:space:]]*label[[:space:]]+.*([[:space:]]|$)+' /tftpboot/nbi_img/pxelinux.cfg/default | grep -A1 -E "label[[:space:]]+drbl([[:space:]]|$)+"
     # 10:label drbl
     # 17:label local
     # so we know we can replace the one between line no. 10 and 17
     between_lines=$(grep -Ei -n "^[[:space:]]*label[[:space:]]+.*([[:space:]]|$)+" $PXE_CONF_TMP | grep -E -A1 "label[[:space:]]+$IMG_NAME([[:space:]]|$)+" | cut -d":" -f1)
     begin_line=$(echo $between_lines | awk -F" " '{print $1}')
     end_line=$(echo $between_lines | awk -F" " '{print $2}')
     # if end_line is nothing, it must be the last block, i.e. we can not find the next [.*]
     if [ -z "$end_line" ]; then
       end_line=$(wc -l $PXE_CONF_TMP | awk -F" " '{print $1}')
     else
       # if not nothing, backword one line
       end_line=$(($end_line - 1))
     fi
     echo "$begin_line $end_line"
}

check_img_in_pxe_cfg() {
     local IMG="$1"
     local PXE_CONF_TMP="$2"
     if [ -z "$IMG" ]; then
        [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
        echo "You must specify the image name! Program terminated!!!"
        [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        exit 1
     fi
     if ! grep -Eiq "^[[:space:]]*label[[:space:]]+$IMG" $PXE_CONF_TMP; then
        [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
        echo "Unable to find the image ($IMG) label! Make sure the $IMG is labeled in $PXE_CONF_TMP! Program terminated!!!"
        [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        exit 1
     fi
}

sub_default_pxe_img() {
     local IMG="$1"
     local PXE_CONF_TMP="$2"
     local MENU_LABEL="$3"
     check_img_in_pxe_cfg $IMG $PXE_CONF_TMP
     # turn off all MENU DEFAULT
     echo "Turn off all MENU DEFAULT... "
     perl -pi -e "s/^(#|[[:space:]])*MENU DEFAULT.*/  # MENU DEFAULT/i" $PXE_CONF_TMP
     all_label="$(awk '/^[[:space:]]*label[[:space:]]+.*([[:space:]]|$)+/ {print $2}' $PXE_CONF_TMP)"
     # 2006/09/10 Steven commented this, we'd better keep the way pxelinux config is.
     #for i in $all_label; do
     #  if [ -z "$(echo $IMG_SHOW_IN_MENU | grep -i $i)" ]; then
     #    [ -n "$VERBOSE" ] && echo "Hide image $i"
     #    hide_reveal_pxe_img $i hide $PXE_CONF_TMP
     #  fi
     #done

     # turn on MENU DEFAULT & turn off MENU HIDE for the specified image
     lines=$(get_pxecfg_image_block $IMG $PXE_CONF_TMP)
     begin_line=$(echo $lines | awk -F" " '{print $1}')
     end_line=$(echo $lines | awk -F" " '{print $2}')
     echo "Make \"$IMG\" as default label in $PXE_CONF_TMP."
     sub_def_cmd="if ($begin_line..$end_line) {s/^(#|[[:space:]])*MENU DEFAULT.*/  MENU DEFAULT/i}"
     sub_hide_cmd="if ($begin_line..$end_line) {s/^(#|[[:space:]])*MENU HIDE.*/  # MENU HIDE/i}"
     perl -pi -e "$sub_def_cmd" $PXE_CONF_TMP
     perl -pi -e "$sub_hide_cmd" $PXE_CONF_TMP

     if [ -n "$MENU_LABEL" ]; then
       echo The MENU LABEL is \"$MENU_LABEL\"
       sub_menu_label_cmd="if ($begin_line..$end_line) {s/^[[:space:]]*MENU LABEL.*/  MENU LABEL $MENU_LABEL/i}"
       perl -pi -e "$sub_menu_label_cmd" $PXE_CONF_TMP
     fi
}
# use script /opt/drbl/bin/hide_reveal_pxe_img instead of fnction.
#hide_reveal_pxe_img() {
#     local IMG="$1"
#     local ACT="$2"
#     local PXE_CONF_TMP="$3"
#     check_img_in_pxe_cfg $IMG $PXE_CONF_TMP
#     # turn off MENU DEFAULT & turn on MENU HIDE for the specified image
#     lines=$(get_pxecfg_image_block $IMG $PXE_CONF_TMP)
#     begin_line=$(echo $lines | awk -F" " '{print $1}')
#     end_line=$(echo $lines | awk -F" " '{print $2}')
#     case "$ACT" in
#       "hide")
#         [ -n "$VERBOSE" ] && echo "Hide $IMG in $PXE_CONF_TMP... "
#         sub_act_cmd="if ($begin_line..$end_line) {s/^(#|[[:space:]])*MENU HIDE.*/  MENU HIDE/i}"
#         ;;
#       "reveal")
#         [ -n "$VERBOSE" ] && echo "Reveal $IMG in $PXE_CONF_TMP... "
#         sub_act_cmd="if ($begin_line..$end_line) {s/^(#|[[:space:]])*MENU HIDE.*/  # MENU HIDE/i}"
#         ;;
#     esac
#     perl -pi -e "$sub_act_cmd" $PXE_CONF_TMP
#}
#
delete_label_block_pxe_img() {
     local LABEL="$1"
     local PXE_CONF_TMP="$2"
     [ -z "$LABEL" -o -z "$PXE_CONF_TMP" ] && return 1
     lines=$(get_pxecfg_image_block $LABEL $PXE_CONF_TMP)
     begin_line=$(echo $lines | awk -F" " '{print $1}')
     end_line=$(echo $lines | awk -F" " '{print $2}')
     # delete lines between $begin_line and $end_line
     perl -i -ne "print unless $begin_line..$end_line" $PXE_CONF_TMP
     return 0
}

ask_lang_set() {
  local language_opt="$1"
  # lang will be the global variable
  # get the language
  if [ -z "$language_opt" ]; then
    # Try the Environment variable "LANG"
    case "$LANG" in
       zh_TW.BIG5)
          lang_answer="zh_TW.BIG5"
          ;;
       zh_TW.UTF-8)
          lang_answer="zh_TW.UTF-8"
          ;;
       *)
          if [ -n "$LANG" -a -e "$DRBL_SCRIPT_PATH/lang/bash/$LANG" ] ; then
            lang_answer="$LANG"
          else
            lang_answer="en_US"
          fi
    esac
  elif [ "$language_opt" = "ask" -o "$language_opt" = "a" ]; then
    # Language
    echo "Language | y| 語言 ?".
    echo "[0]: English"
    echo "[1]: Traditional Chinese (Big5) - Taiwan |  (jX) - xW".
    echo "[2]: Traditional Chinese (UTF-8, Unicode) - Taiwan | 中文 (萬國標準碼) - 台灣"
    echo -n "[0] "
    read lang_answer
    [ -z "$lang_answer" ] && lang_answer="0"
  else
    lang_answer="$language_opt"
  fi
} # end of ask_lang_set

load_lang_set() {
  lang_answer=$1
  # The language files are en, tw.BIG5 and tw.UTF-8, we have to format the input parameter.
  case "$lang_answer" in
     1|zh_TW.BIG5|zh_TW.big5|tw.BIG5)
        lang="zh_TW.BIG5"
        ;;
     2|zh_TW.UTF-8|zh_TW.utf8|tw.UTF-8|tw.utf8)
        lang="zh_TW.UTF-8"
        ;;
     *)
        if [ -n "$lang_answer" -a -e "$DRBL_SCRIPT_PATH/lang/bash/$lang_answer" ] ; then
          lang="$lang_answer"
        else
          lang="en_US"
        fi
  esac
  
  # get the l10n message
  if [ -n "$lang" -a -e "$DRBL_SCRIPT_PATH/lang/bash/$lang" ] ; then
    . $DRBL_SCRIPT_PATH/lang/bash/$lang
  else
    echo "Not such language option!!!"
    exit 1
  fi
} # end of load_lang_set
#
ask_and_load_lang_set() {
  local language_opt="$1"
  ask_lang_set $language_opt
  load_lang_set $lang_answer
} # end of ask_and_load_lang_set

#
language_help_prompt_by_idx_no() {
  echo "-l, --language INDEX Set the language to be shown by index number:"
  echo "       [0|en]: English,"
  echo "       [1|tw.BIG5|zh_TW.BIG5]: Traditional Chinese (Big5) - Taiwan,"
  echo "       [2|tw.UTF-8|zh_TW.UTF-8]: Traditional Chinese (UTF-8, Unicode) - Taiwan"
  echo "       [a|ask]: Prompt to ask the language index"
}
#
language_help_prompt_by_idx_name() {
  echo "-ln  NAME    Set the language to be shown by index name, not number, such as en, tw.BIG5, zh_TW.BIG5, tw.UTF-8, zh_TW.UTF-8"
}

# For update-rc.d in Debian
prepare_update_rc_d_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
  cp -f --parents /usr/sbin/update-rc.d $newroot
  cp -f --parents /usr/bin/perl $newroot
  perl_lib_need=$(ldd /usr/bin/perl | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
  for imod in $perl_lib_need; do
    cp --parents $imod $newroot
  done
  # Ugly! we need some perl pm...
  mkdir -p $newroot/usr/share
  rsync -a /usr/share/perl $newroot/usr/share/
}

#
clean_update_rc_d_env() {
  local newroot="$1"
  [ -z $newroot ] && exit 1
  # we can clean files in /usr/bin and /usr/sbin in common_root because they are mount ponts.
  [ -f $newroot/usr/bin/perl ] && rm -f $newroot/usr/bin/perl 
  [ -f $newroot/usr/sbin/update-rc.d ] && rm -f $newroot/usr/sbin/update-rc.d
  perl_lib_need=$(ldd /usr/bin/perl | sed -e "s/.*=> //g" -e "s/(0x.*)//g" | awk '{print $1}')
  for f2rm in $perl_lib_need; do
    [ -f $newroot/$f2rm ] && rm -f $newroot/$f2rm
  done
}

#
chk_deb_installed () {
  local pkg_name=$1
  local RC
  [ -z "$pkg_name" ] && exit 1
  dpkg-query --print-avail $pkg_name &>/dev/null
  RC=$?
  return $RC
}
#
copy_rc1d_for_drbl_ssi() {
  # Note! use absolute path for template_cli 
  local template_cli="$1"
  echo -n "Copying files to $drbl_common_root/drbl_ssi for DRBL SSI... "
  mkdir -p "$drbl_common_root/drbl_ssi/rc1.d"
  if [ -z "$template_cli" ]; then
    for ih in $drblroot/*; do
      # use the 1st one drbl client we found as template
      if [ -d "$ih" ]; then
        template_cli="$ih"
        break
      fi
    done
  fi
  cp -af $template_cli/$RCX_ROOTDIR/rc1.d/* $drbl_common_root/drbl_ssi/rc1.d/
  echo "done!"
} # end of copy_rc1d_for_drbl_ssi
#
remove_rc1d_for_drbl_ssi() {
  # Note! use absolute path for template_cli 
  local template_cli="$1"
  echo -n "Resetting files of $drbl_common_root/drbl_ssi for DRBL SSI... "
  # we just clean the unnecessary files, keep the directory
  if [ -d "$drbl_common_root/drbl_ssi/rc1.d" ]; then
    rm -rf $drbl_common_root/drbl_ssi/rc1.d
  fi
  echo "done!"
} # end of remove_rc1d_for_drbl_ssi
#
get_gdm_kdm_conf_filename() {
  # Get the gdm, kdm config:
  # Ex: this is the setting for gdm 2.13 before and kdm 2.x/3.x
  # GDM_CFG="/etc/X11/gdm/gdm.conf"
  # FAC_GDM_CFG="/etc/X11/gdm/factory-gdm.conf"
  # KDM_CFG="/etc/kde3/kdm/kdmrc"
  # For gdm 2.13 or later, different filenames, such as custom.conf, gdm.conf-custom and kdmrc, no more factory-gdm.conf
  GDM_CFG=""
  # The order "gdm.conf-custom custom.conf gdm.conf" is important, we put gdm.conf as the last one, since we wan to get only one $GDM_CFG, gdm.conf is always the last one to choose, if we can get gdm.conf-custom or custom.conf, we use that first
  # Note: Even in SuSE, gdm.conf/custom.conf is in /etc/opt/gnome/gdm/gdm.conf, we can get it by rpm -ql gdm, unlike kdm. It's not necessary to put /etc/ in the beginning.
  for ig in gdm.conf-custom custom.conf gdm.conf; do
    GDM_CFG="$($query_pkglist_cmd gdm 2>/dev/null | grep -E "\/$ig$")"
    [ -n "$GDM_CFG" ] && break
  done
  FAC_GDM_CFG="$($query_pkglist_cmd gdm 2>/dev/null | grep -E "\/factory-gdm.conf$")"

  # kdmrc maybe in package kdm (Debian-based), kdebase (FC), kdebase-kdm-config-file (Mandrake) or kdebase3-kdm (OpenSuSE)
  # In FC, there are 2 kdmrc:
  # /etc/X11/xdm/kdmrc
  # /etc/kde/kdm/kdmrc
  # But /etc/X11/xdm/kdmrc is actually linked to /etc/kde/kdm/kdmrc, so use either one will be ok.
  for ipkg in kdm kdebase kdebase-kdm-config-file kdebase3-kdm; do
    KDM_CFG="$($query_pkglist_cmd $ipkg 2>/dev/null | grep -E "(\/kdmrc$)" | head -n 1)"
    if [ -n "$(echo $KDM_CFG | grep -E "^\/opt\/kde")" ]; then
      # For SuSE 10.1 or earlier, actually the config file is in /etc/opt/kde3/share/config/kdm/kdmrc, therefore put /etc/ in the beginning.
      # For SuSE 10.2, no more /etc/opt/kde3/share/config/kdm/kdmrc, only /opt/kde3/share/config/kdm/kdmrc
      # Ref: http://lists.opensuse.org/opensuse-factory/2006-09/msg00021.html
      [ -e "/etc/$KDM_CFG" ] && KDM_CFG=/etc/$KDM_CFG
    fi
    [ -n "$KDM_CFG" ] && break
  done
} # end of get_gdm_kdm_conf_filename
#
get_block_line_in_gdm_kdm() {
  local session="$1"
  local CFG_FILE="$2"
  [ -f "$CFG_FILE" ] || exit 1
  # Take xdmcp as an example:
  # Turn on the Enable=true in [xdmcp]
  # Enable=true
  # By using
  # grep -n "^\[.*\]" gdm.conf |grep -A1 "\[xdmcp\]"                 
  # We can get the results like:
  # 175:[xdmcp]
  # 210:[gui]
  # so we know we can replace the one between line no. 175 and 210
  between_lines=$(grep -n "^\[.*\]" $CFG_FILE |grep -i -A1 "\[$session\]" | cut -d":" -f1)
  begin_line=$(echo $between_lines | awk -F" " '{print $1}')
  end_line=$(echo $between_lines | awk -F" " '{print $2}')
  # if end_line is nothing, it must be the last block, i.e. we can not find the next [.*]
  if [ -z "$end_line" ]; then
    end_line=$(wc -l $CFG_FILE | awk -F" " '{print $1}')
  else
    # if not nothing, backword one line
    end_line=$(($end_line - 1))
  fi
  echo "$begin_line $end_line"
} # end of get_block_line_in_gdm_kdm

#
get_dhcpdlease_dir() {
  if [ -e /etc/debian_version ]; then
    # Debian
    DHCPDLEASE_DIR="/var/lib/dhcp3"
  elif [ -e /etc/SuSE-release ]; then
    # SuSE
    DHCPDLEASE_DIR="/var/lib/dhcp/db"
  else
    # RH-like
    # FC5: /var/lib/dhcpd/dhcpd.leases
    # RH 8.0/9, FC1-4: /var/lib/dhcp/dhcpd.leases
    # So this method is better:
    DHCPDLEASE_DIR="$(dirname `rpm -ql dhcp | grep -E "dhcpd.leases$"`)"
  fi
}
#
countdown () {
 local time_limit="$1"
 local i
       ( i="$time_limit"
         while [ "$i" -ne 0  ]; do
           echo -n "$i "
           sleep 1
           i=$((i-1))
         done
       )
}
#
is_drbl_client() {
  root_src="$(LC_ALL=C mount | grep -Ew "on /" | awk -F" " '{print $1}')"
  # Example for $root_src: 192.168.50.254:/tftpboot/node_root
  if [ -n "$(echo $root_src | grep -E "^[[:space:]]*([[:digit:]]+\.){3}[[:digit:]]+:/.*")" ] ; then
   rc=0
  else
   rc=1
  fi
  return $rc
}
#
add_param_in_pxelinux_cfg_drbl_related_block() {
  local PXE_CONF="$1"
  local ramdisk_opt="$2"
  [ -z "$ramdisk_opt" ] && echo "You have to assign ramdisk_opt!" && return 1
  for iblock in drbl drbl-terminal; do
    lines=$(get_pxecfg_image_block $iblock $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:]]+$ramdisk_opt([[:space:]]+|$)")"
    if [ -z "$tag_found" ]; then
      sub_menu_label_cmd="if ($begin_line..$end_line) {s/(^[[:space:]]*append[[:space:]]+.*)/\$1 $ramdisk_opt/i}"
      perl -pi -e "$sub_menu_label_cmd" $PXE_CONF
    fi
  done
} # end of add_param_in_pxelinux_cfg_drbl_related_block
#
del_param_in_pxelinux_cfg_drbl_related_block() {
  local PXE_CONF="$1"
  local ramdisk_opt="$2"
  [ -z "$ramdisk_opt" ] && echo "You have to assign ramdisk_opt!" && return 1
  for iblock in drbl drbl-terminal; do
    lines=$(get_pxecfg_image_block $iblock $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:]]+$ramdisk_opt([[:space:]]+|$)")"
    if [ -n "$tag_found" ]; then
      sub_menu_label_cmd="if ($begin_line..$end_line) {s/$ramdisk_opt//i}"
      perl -pi -e "$sub_menu_label_cmd" $PXE_CONF
    fi
  done
} # end of del_param_in_pxelinux_cfg_drbl_related_block
#
set_drbl_ocs_extra_param() {
local mode=$1
OCS_PARAM_TMP=$2
local OCS_OPTS

if [ "$mode" = "restore" ]; then
  $DIA --separate-output --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --checklist "$msg_choose_param_to_set \n$msg_hint_for_not_fdisk:" \
  0 0 0 $DIA_ESC \
  "-g auto"  "$msg_ocs_param_g_auto" on \
  "-x"       "$msg_ocs_param_x" on \
  "-brdcst"  "$msg_ocs_param_broadcast" off \
  "-hn0 $WIN_HOSTNAME_PREFIX"  "$msg_ocs_param_hn0" off \
  "-hn1 $WIN_HOSTNAME_PREFIX"  "$msg_ocs_param_hn1" off \
  "-k"       "$msg_ocs_param_k" off \
  "-r"       "$msg_ocs_param_r" off \
  "-v"       "$msg_ocs_param_v" off \
  "-nogui"   "$msg_ocs_param_nogui" off \
  "-c"       "$msg_ocs_param_c" off \
  "-u"       "$msg_ocs_param_u" off \
  "-t"       "$msg_ocs_param_t" off \
  "-j0"      "$msg_ocs_param_j0" off \
  "-ns"      "$msg_ocs_param_ns" off \
  "-e"       "$msg_ocs_param_e" off \
  "-f"       "$msg_ocs_param_f" off \
  "-s"       "$msg_ocs_param_s" off \
  "-a"       "$msg_ocs_param_a" off \
  "-o0"      "$msg_ocs_param_o0" off \
  "-o1"      "$msg_ocs_param_o1" off \
  2> $OCS_PARAM_TMP

  # about y[0-2]
  $DIA --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --menu "$msg_if_always_provide_clonezilla_srv" \
  0 0 0 $DIA_ESC \
  ""          "$msg_ocs_param_skip" \
  "-y0 "      "$msg_ocs_param_y0" \
  "-y1 "      "$msg_ocs_param_y1" \
  "-y2 "      "$msg_ocs_param_y2" \
  2>> $OCS_PARAM_TMP
else
  # save
  $DIA --separate-output --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --checklist "$msg_choose_param_to_set:" \
  0 0 0 $DIA_ESC \
  "-q"         "$msg_ocs_param_q" on \
  "-c"         "$msg_ocs_param_c" off \
  "-nogui"     "$msg_ocs_param_nogui" off \
  "-a"         "$msg_ocs_param_a" off \
  "-f"         "$msg_ocs_param_f" off \
  "-s"         "$msg_ocs_param_s" off \
  "-ntfs-ok"   "$msg_ocs_param_notfs_ok" off \
  2> $OCS_PARAM_TMP
fi

$DIA --backtitle "$msg_nchc_free_software_labs" --title  \
"$msg_clonezilla_advanced_extra_param" --menu "$msg_choose_post_mode_after_clone:" \
0 0 0 $DIA_ESC \
"-p reboot "    "$msg_ocs_param_p_reboot" \
"-p poweroff "  "$msg_ocs_param_p_poweroff" \
"-p choose "    "$msg_ocs_param_p_choose" \
"-p true "      "$msg_ocs_param_p_true" \
2>> $OCS_PARAM_TMP

# if -hn0|-hn1 is chosen, 
# (1). prompt the warning about ntfs-3g/nfsmount for ntfs is necessary
# (2). ask if the hostname prefix want to change
if grep -qE "\-hn[01]" $OCS_PARAM_TMP; then
  # part 1.
  $DIA --title "$msg_change_hostname_of_MS_WIN_on_the_fly" --clear \
       --msgbox "$msg_write_MS_WIN_is_necessary" 15 70

  # part 2.
  HNTMP="$(mktemp /tmp/winhn.XXXXXX)"
  $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
  --inputbox "$msg_What_the_win_hostname_prefix ?" 0 0 "$WIN_HOSTNAME_PREFIX" 2> $HNTMP
  HN_PREFIX="$(cat $HNTMP)"
  if [ -z "$HN_PREFIX" ]; then
     echo "You did not specify the hostname prefix for M$ windows! We use the default value in drbl-ocs.conf"
  else
     perl -pi -e "s/(-hn.*) $WIN_HOSTNAME_PREFIX/\$1 $HN_PREFIX/g" $OCS_PARAM_TMP
  fi
  [ -f "$HNTMP" ] && rm -f $HNTMP
fi

if [ "$mode" = "save" ]; then
  # save
  $DIA --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --menu "$msg_choose_one_compression_param_to_save" \
  0 0 0 $DIA_ESC \
  "-z1 "      "$msg_ocs_param_z1" \
  "-z2 "      "$msg_ocs_param_z2" \
  "-z3 "      "$msg_ocs_param_z3" \
  "-z0 "      "$msg_ocs_param_z0" \
  2>> $OCS_PARAM_TMP
fi
# force to strip the unnecessary quotation ', this is specially for dialog (from cdialog) in Mandriva. Otherwise it will cause -p reboot become (containing space) '-p reboot', which is wrong option in dcs.
LC_ALL=C perl -pi -e "s/\'//g" $OCS_PARAM_TMP

} # end of set_drbl_ocs_extra_param

#
set_ocs_sr_extra_param() {
# This function is used to be called inside ocs-sr, not from other program.
local mode=$1
OCS_PARAM_TMP=$2
local OCS_OPTS

if [ "$mode" = "restore" ]; then
  # Although -u/-y0/-y1 is shown in ocs-sr, but actually they are used to
  # pass the varialbe, and in this function, when chooing parameters, we
  # remove them on purpose. Since the necessay parameters will be assigned by
  # drbl-ocs (Ex: we use "-y1 -p choose" in drbl-ocs)
  $DIA --separate-output --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --checklist "$msg_choose_param_to_set \n$msg_hint_for_not_fdisk:" \
  0 0 0 $DIA_ESC \
  "-g auto"  "$msg_ocs_param_g_auto" on \
  "-hn0 $WIN_HOSTNAME_PREFIX"  "$msg_ocs_param_hn0" off \
  "-hn1 $WIN_HOSTNAME_PREFIX"  "$msg_ocs_param_hn1" off \
  "-k"       "$msg_ocs_param_k" off \
  "-r"       "$msg_ocs_param_r" off \
  "-v"       "$msg_ocs_param_v" off \
  "-nogui"   "$msg_ocs_param_nogui" off \
  "-c"       "$msg_ocs_param_c" on \
  "-t"       "$msg_ocs_param_t" off \
  "-j0"      "$msg_ocs_param_j0" off \
  "-e"       "$msg_ocs_param_e" off \
  "-a"       "$msg_ocs_param_a" off \
  "-o0"      "$msg_ocs_param_o0" off \
  "-o1"      "$msg_ocs_param_o1" off \
  2> $OCS_PARAM_TMP
else
  # save
  $DIA --separate-output --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --checklist "$msg_choose_param_to_set:" \
  0 0 0 $DIA_ESC \
  "-q"       "$msg_ocs_param_q" on \
  "-c"       "$msg_ocs_param_c" on \
  "-nogui"   "$msg_ocs_param_nogui" off \
  "-a"       "$msg_ocs_param_a" off \
  "-ntfs-ok" "$msg_ocs_param_notfs_ok" off \
  2> $OCS_PARAM_TMP
fi

# if -hn0|-hn1 is chosen, 
# (1). prompt the warning about ntfs-3g/nfsmount for ntfs is necessary
# (2). ask if the hostname prefix want to change
if grep -qE "\-hn[01]" $OCS_PARAM_TMP; then
  # part 1.
  $DIA --title "$msg_change_hostname_of_MS_WIN_on_the_fly" --clear \
       --msgbox "$msg_write_MS_WIN_is_necessary" 15 70

  # part 2.
  HNTMP="$(mktemp /tmp/winhn.XXXXXX)"
  $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
  --inputbox "$msg_What_the_win_hostname_prefix ?" 0 0 "$WIN_HOSTNAME_PREFIX" 2> $HNTMP
  HN_PREFIX="$(cat $HNTMP)"
  if [ -z "$HN_PREFIX" ]; then
     echo "You did not specify the hostname prefix for M$ windows! We use the default value in drbl-ocs.conf"
  else
     perl -pi -e "s/(-hn.*) $WIN_HOSTNAME_PREFIX/\$1 $HN_PREFIX/g" $OCS_PARAM_TMP
  fi
  [ -f "$HNTMP" ] && rm -f $HNTMP
fi

if [ "$mode" = "save" ]; then
  # save
  $DIA --backtitle "$msg_nchc_free_software_labs" --title  \
  "$msg_clonezilla_advanced_extra_param" --menu "$msg_choose_one_compression_param_to_save" \
  0 0 0 $DIA_ESC \
  "-z1 "      "$msg_ocs_param_z1" \
  "-z2 "      "$msg_ocs_param_z2" \
  "-z3 "      "$msg_ocs_param_z3" \
  "-z0 "      "$msg_ocs_param_z0" \
  2>> $OCS_PARAM_TMP
fi
# force to strip the unnecessary quotation ', this is specially for dialog (from cdialog) in Mandriva. Otherwise it will cause -p reboot become (containing space) '-p reboot', which is wrong option in dcs.
LC_ALL=C perl -pi -e "s/\'//g" $OCS_PARAM_TMP

} # end of set_ocs_sr_extra_param
#
check_DIA_set_ESC(){
  # function to check dialog/Xdialog/whiptail
  # DIA_ESC is global variable, 
  # man dialog:
  # A "--" by itself is used as an escape, i.e., the next token on the com-
  # mand-line is not treated as an option.
  # dialog --title -- --Not an option
  # 3 cases if those Not an option is like (-g auto, -x...):
  # (1) dialog can go with or without --
  # (2) Xdialog can NOT go with --
  # (3) whiptail can go only with --
  local dia_="$1"
  # check DIA
  if ! type $dia_ &>/dev/null; then
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "$dia_: command not found!"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     echo "Program terminated!!!"
     exit 1
  fi
  case "$dia_" in
    dialog|[Xgk]dialog) DIA_ESC="" ;;
    whiptail) DIA_ESC="--" ;;
  esac
} # end of check_DIA_set_ESC
#
check_if_run_in_drbl_server() {
  local prog="$1"
  # check if it s run in drbl server
  if [ ! -d "$PXELINUX_DIR" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "[$LOGNAME] You should run this program $prog in DRBL server, NOT in DRBL client or other machine."
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "Program terminated!"
    exit 1
  fi
}
dialog_like_prog_help_prompt() {
   echo " -d0, --dialog         Use dialog"
   echo " -d1, --Xdialog        Use Xdialog"
   echo " -d2, --whiptail       Use whiptail"
   echo " -d3, --gdialog        Use gdialog"
   echo " -d4, --kdialog        Use kdialog"
}
root_over_nfs() {
  local root_over_nfs
  root_over_nfs="$(grep -Ew "^[[:space:]]*([[:digit:]]+\.){3}[[:digit:]]+:/tftpboot/node_root([[:space:]]+|$)" /proc/mounts | awk -F " " '{print $2}')"
  if [ "$root_over_nfs" = "/" ]; then
    echo "yes"
    rc=0
  else
    echo "no"
    rc=1
  fi
  return $rc
}
#
get_dir_filesystem(){
  # function to show the filesystem of input dir
  local target_dir="$1"
  local df_list
  [ -z "$target_dir" ] && return 1
  # merge the  df -T output as one line, otherwise for some version of df with nfs, the results is in 2 lines like:
  # [root@co50102 tmp]# LC_ALL=C df -T /home/partimag/
  #    Filesystem    Type   1K-blocks      Used Available Use% Mounted on
  #    192.168.205.254:/home
  #                   nfs     7395808   3855328   3158752  55% /home
  df_list="$(LC_ALL=C df -T $target_dir | tail -n +2)"
  echo $df_list | awk -F" " '{print $2}'
}

# Copyright (C) 2007 Bryan McLellan <btm@loftninjas.org>
# Licensed under the GNU GPL version 2 or greater, see COPYING
# generate a random password of length passed out of characters in array char. returns password in $random_password
# replaces external perl password generation script
# Modified by Steven Shiau, remove 0, 1, add [ ] ^ ! $ %
make_random_password(){ 
  local randchar=""
  [ -z "$1" ] && lenpass=6 || lenpass=$1

  # To avoid confusing, no 0, 1 for passwd, the O (oh) and l (L) will be clear. 
  char=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 2 3 4 5 6 7 8 9 [ ] ^ ! $ %)
  lenchars=${#char[*]}

  for (( x=0 ; x<${lenpass} ; x+=1 ));
  do
    let rand=${RANDOM}%${lenchars}
    randchar=${randchar}${char[$rand]}
  done

  random_password=${randchar}
}
#
is_boot_from_live() {
  # function to check if the running OS is boot from live cd/usb stick ?
  if [ -e /etc/debian_version -a \
       -n "$(grep -io "boot=casper" /proc/cmdline)" ]; then
     return 0
  else
     return 1
  fi
}
