# -*-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="$PATH:/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:$DRBL_SCRIPT_PATH/sbin:$DRBL_SCRIPT_PATH/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
   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 -qF "testing/unstable" /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
    # $auto_login_id and $echo_no_digit_0_1 are global variables
    # auto login username is set as hostname
    # use client's hostname as the 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
      . $ihost/$SYSCONF_PATH/network
      auto_login_id="$HOSTNAME"
    fi
    # if no hostname, such as in DRBL SSI mode, try to map the hostname from /etc/hosts, 
    if [ -z "$auto_login_id" ]; then
      ip="$(basename $ihost)"
      auto_login_id="$(grep "^[[:space:]]*$ip\>" /etc/hosts | awk -F" " '{print $NF}' | sort | uniq)"
    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 -q ^$rc1_sulogin $ihost/etc/inittab ; then
      echo "Set the single user password for client ${ihost##/*/}, this will be safer..."
      cat <<-EOF >> $ihost/etc/inittab

# Single User Mode Password
$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 the kernel config \"$kernel_config...\""
  if [ ! -f $kernel_config ]; then 
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$kernel_config does NOT exist!"
    echo "I can not judge wheither 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="$*"
  for ih in $HOSTS; do
    case "$ih" in
      *.*.*.*)
        # list by IP
	# the pxecfg will like "C0A80001"
	 #pxecfg="$(/usr/bin/gethostip $ih | cut -d" " -f3)"
	 pxecfg="$($DRBL_SCRIPT_PATH/bin/gethostip.pl $ih)"
         ;;
      *:*:*:*:*:*)
        # 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"
         ;;
    esac
    echo -n "Generate the PXE config file for host $ih ... "
    cp -f $PXELINUX_DIR/default_skeleton $PXELINUX_DIR/$pxecfg
    echo "done!"
  done
}

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

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
    # Language
    echo "Language | y| 語言 ?".
    echo "[0]: English"
    echo "[1]: Chinese Traditional (Big5) - Taiwan |  (jX) - xW".
    echo "[2]: Chinese Traditional (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
  case "$lang_answer" in
     1)
        lang="tw.BIG5"
        ;;
     2)
        lang="tw.UTF-8"
        ;;
     tw.*)
        lang="$lang_answer"
        ;;
     *)
        lang="en"
  esac
  
  # get the l10n message
  if [ -f "$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  Set the language to be shown by index number"
  echo "                [0]: English, [1]: Chinese Traditional (Big5) - Taiwan, [2] Chinese Traditional (UTF-8, Unicode) - Taiwan"
}
#
language_help_prompt_by_idx_name() {
  echo "-ln             Set the language to be shown by index name, not number, such as en, tw.Big5, 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
  [ -z "$pkg_name" ] && exit 1
  #pkg_status="$(dpkg -s $pkg_name 2>/dev/null | grep "^Installed-Size:")"
  pkg_status="$(dpkg -s $pkg_name 2>/dev/null | grep "^Status: install ok")"
  if [ -z "$pkg_status" ]; then
    # not installed
    RC=1
  else
    # installed
    RC=0
  fi
  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/*
    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/
  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, actually the config file is in /etc/opt/kde3/share/config/kdm/kdmrc, therefore put /etc/ in the beginning.
      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
       )
}
