#!/bin/bash
# Author: Steven Shiau <steven _at_ nchc org tw>
# License: GPL 

# Load DRBL setting and functions
DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/opt/drbl/}"
. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions

#
USAGE() {
   echo "Usage: $0 [OPTION]"
   echo "OPTION"
   language_help_prompt_by_idx_no
   echo "-o, --console-output OPT: Set the console output parameters."
   echo "-i, --pxe-serial-output OPT:  Set the PXE menu to work with serial console output."
   echo "-k, --client-kernel-pkg KERNEL_PKG:  Specify the KERNEL_PKG (rpm or deb) which you want DRBL client to use."
   echo "-e, --extra-client-kernel-pkg EXTRA_KERNEL_PKG:  Specify the extra KERNEL_PKG (rpm or deb) which you want DRBL client to use. Usually this is specially for Ubuntu linux-restricted-modules and SuSE kernel-default-nongpl. Must use this with option -k|--client-kernel-pkg."
   echo "-s, --client-kernel-ver-from-server KERNEL_VER:  Use the kernel version KERNEL_VER from server, i.e. from /boot/vmlinuz-KERNEL_VER and /lib/modules/KERNEL_VER..."
   echo "-c, --no-required-pkgs-check:  Force NOT to check the required packages, assume they are installed."
   echo "-a, --no-prompt-different-arch-pkgs:  Force NOT to show the messages about different architecture packages re-installation in the different CPU arch for server and client."
   echo "-p, --skip-client-kernel-install:  Force NOT to install the kernel for client. This option is used when a kernel for client already exists."
   echo "-r, --only-show-required-pkgs:  Do noting, just show the necessary packages."
   echo "-d, --dev-for-client-is-ready:  Dev files are already ready for clients, do not let $0 check it again."
   echo "-v, --verbose:  Prints verbose information"
} # end of USAGE

check_debian_dev_for_client() {
echo "$msg_delimiter_star_line"
# dev for client machines
# For Debian woody & B2D pureKDE20050603..It's necessary to make one before run
# this... (Just create the tarball from some Debian machine)
# It's ugly here...
# The solution: Make udev as the 1st priority
[ ! -d $drbl_pkgdir ] && mkdir -p $drbl_pkgdir

if ! chk_deb_installed udev; then
  # udev is not available, use tarball
  if [ -f $drbl_setup_path/files/$OS_Version/dev.$OS_Version.tgz ]; then
    echo "Udev is not available, use pre-packaged device files."
    cp -f $drbl_setup_path/files/$OS_Version/dev.$OS_Version.tgz $drbl_pkgdir/dev.tgz
  else
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "No udev, and no pre-packaged device files!!! Program terminated!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    exit 1
  fi
fi
} # end of check_debian_dev_for_client

check_required_pkgs() {
  echo "$msg_delimiter_star_line"
  eval PKG_FROM_OS=\$PKG_FROM_${OS_type}
  echo "$msg_necessary_pkgs_from_dists"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  echo "$PKG_FROM_OS $PKG_DEV" 
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  echo "$msg_delimiter_star_line"
  echo "$msg_necessary_pkgs_from_drbl"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  echo "$PKG_FROM_DRBL"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  echo "$msg_delimiter_star_line"
  echo "$msg_recommended_pkgs_from_dists"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  echo "$PKG_TO_QUERY"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  # if it's only show required packages, we exit here.
  [ "$only_show_required_pkgs" = "yes" ] && exit 0
  echo "$msg_delimiter_star_line"
  echo -n "$msg_press_ctrl_c_stop Or $msg_press_enter_to_continue "
  read
  echo "Check if they are installed... "
  pkg_not_installed=""
  ret=0
  for ipkg in $PKG_FROM_OS $PKG_FROM_DRBL $PKG_DEV; do
    echo -n "Checking $ipkg... "
    if ! $query_pkglist_exist_cmd $ipkg &>/dev/null; then
      pkg_not_installed="$pkg_not_installed $ipkg"
      ret=$((ret + 1))
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "NOT installed!"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    else
      [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
      echo "installed."
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    fi
  done
  if [ "$ret" -gt 0 ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo "$msg_some_required_pkgs_not_installed: $pkg_not_installed."
    echo "$msg_use_drblsrv_instead"
    echo "$msg_continue_may_not_work"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "$msg_are_u_sure_u_want_to_continue"
    echo -n "[y/N] "
    read continue_run_ans
    case "$continue_run_ans" in
       y|Y|[yY][eE][sS])
         echo "$msg_ok_let_do_it"
         ;;
       *)
         echo "$msg_program_stop"
         exit 1
         ;;
    esac
   fi
} # end of check_required_pkgs
#
check_installed_pkgs_arch() {
  echo "$msg_delimiter_star_line"
  case "$OS_type" in
       RH)
         echo -n "Checking the installed packages of different arch..."
         reinstall_pkg=""
         for ipkg in $i686_pkg_check_list_RH_like; do
           if rpm -q $ipkg &>/dev/null; then
             echo -n "."
             installed_pkg="$(rpm -qa | grep -E "^${ipkg}-[0-9]" | pkg-ver-latest)"
             reinstall_pkg="$reinstall_pkg $installed_pkg.i386.rpm"
           fi
         done
         echo " done!"
         [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
         echo "$msg_install_glibc_etc_arch_by_yourself"
         echo "rpm -Uvh --force $reinstall_pkg"
         [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
         echo -n "$msg_press_ctrl_c_stop Or $msg_press_enter_to_continue"
         read 
         ;;
       SUSE)
         echo -n "Checking the installed packages of different arch..."
         reinstall_pkg=""
         for ipkg in $i686_pkg_check_list_SUSE; do
           if rpm -q $ipkg &>/dev/null; then
             echo -n "."
             installed_pkg=`rpm -qa | grep ^${ipkg}-[0-9]`
             reinstall_pkg="$reinstall_pkg $installed_pkg.i586.rpm"
           fi
         done
         echo " done!"
         [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
         echo "$msg_install_glibc_etc_arch_by_yourself"
         echo "rpm -Uvh --force $reinstall_pkg"
         [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
         echo -n "$msg_press_ctrl_c_stop Or $msg_press_enter_to_continue"
         read 
         ;;
       MDK|MDV|DBN)
         # Not necessary, since only single arch package in this distribution
         true
         ;;
  esac
} # end of check_installed_pkgs_arch
#
install_kernel_for_client_func() {
  # Create the necessary dirctories, UGLY...
  # the /tftpboot/node_root/tmp/boot is for link only, refer to the /tftpboot/node_root/etc/rc.d/rc.sysinit, the code "# For DRBL, create the directory /tmp/boot, so that the link /tmp/boot /boot...
  # /var/lib/rpm is necessary when try to install kernel for client with rpm --force ... in MDK. It need to create the lock file /tftpboot/node_root//var/lib/rpm/RPMLOCK
  mkdir -p $drbl_common_root/tmp/boot/ $drbl_common_root/var/{lib,tmp} $drbl_common_root/var/lib/rpm $drbl_common_root/lib/modules/
  (cd $drbl_common_root; ln -fs tmp/boot boot)

  echo "$msg_delimiter_star_line"
  echo "$msg_install_kernel_for_clients ... "
  case "$client_krn_mode" in
     "from_pkg")
        echo "The kernel for client is from specific package $client_kernel_pkg."
        install-kernel-for-client $verbose_opt -l $lang -k $client_kernel_pkg
        rc=$?
	# We install the extra kernel package (linux-restricted-modules and linux-ubuntu-modules (from ubuntu 7.10) ) after the main kernel since if depmod is later, we will see some warning like:
	# WARNING: /lib/modules/2.6.17-10-386/madwifi/ath_rate_sample.ko needs unknown symbol ath_hal_computetxtime
	# Ubuntu restricted kernel is mounted in /lib/modules/$KVER/volatile/ when booting (/etc/init.d/linux-restricted-modules-common), and depmod will be run by /sbin/lrm-manager about the mounting.
	# From ubuntu 7.10, another kernel module is linux-ubuntu-modules.
	if [ -n "$extra_client_kernel_pkg" ]; then
          for i in $extra_client_kernel_pkg; do
            if [ -n "$(echo $i | grep linux-restricted-modules)" ]; then
              install_extra_opt="--just-cp-files"
	    else
              install_extra_opt=""
            fi
	    install-kernel-for-client $verbose_opt -l $lang $install_extra_opt -k $i
          done
	fi
        ;;
     "from_server")
        echo "The kernel for client is copied from server."
        install-kernel-for-client $verbose_opt -l $lang -s $client_kernel_ver_from_server
        rc=$?
        ;;
  esac
  # check if kernel installed successfully
  if [ "$rc" -gt 0 ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "Failed to install the kernel for client!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "$msg_program_stop"
    exit 1
  fi
} # end of install_kernel_for_client_func
#
check_input_kernel_param() {
  if [ -n "$client_kernel_pkg" ]; then
     # check if the kernel package exists
     if [ ! -f "$client_kernel_pkg" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "$client_kernel_pkg does NOT exist!!!"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      echo "$msg_program_stop"
      exit 1
     fi
     client_krn_mode="from_pkg"
     echo "Kernel package $client_kernel_pkg is used for client..."
  elif [ -n "$client_kernel_ver_from_server" -a \
         -f /boot/vmlinuz-$client_kernel_ver_from_server -a \
         -d /lib/modules/$client_kernel_ver_from_server ]; then
     client_krn_mode="from_server"
     echo "Using kernel from this server for client..."
  else
     [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
     echo "You must provide the kernel package (rpm or deb) or the kernel in the server"
     [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
     USAGE
     exit 1
  fi
  # 1. $PKG_FROM_* and $PKG_FROM_DRBL are loaded from conf/drbl.conf
  # TODO: how about kernel 2.4 ? no udev ? how to prompt them ?
  # If the kernel is version 2.6 for client, we must use udev.
  # For package name, like: linux-image-2.6.15-26-686_2.6.15-26.46_i386.deb
  #                    FC5: kernel-2.6.17-1.2174_FC5.i686.rpm
  #                    RH9: kernel#2.4.20-46.9.legacy_2.4.20-46.9.legacy_i586.rpm
  # For kernel version like: 2.6.15-26-386
  if [ -n "$(echo $client_kernel_pkg | grep -iE "[_-](2\.6\.|3\.)[[:digit:]]+")" -o \
       -n "$(echo $client_kernel_ver_from_server | grep -iE "^(2\.6\.|3\.)[[:digit:]]+")" ]; then 
    PKG_DEV="udev"
  else
    if [ "$dev_for_client_is_ready" = "no" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "This script `basename $0` only works in kernel 2.6 or 3 or later with udev supported! The running kernel for client is not kernel 2.6 or 3 or later! Program terminated!"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      exit 1
    fi
  fi
} # end of check_input_kernel_param


#################
####   Main  ####
#################
# some default settings
required_pkgs_check="yes"
prompt_different_arch_pkgs="yes"
install_client_kernel="yes"
only_show_required_pkgs="no"
dev_for_client_is_ready="no"
extra_client_kernel_pkg=""

# Parse command-line options
while [ $# -gt 0 ]; do
  case "$1" in
    -l|--language)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
	  specified_lang="$1"
	  shift
        fi
	[ -z "$specified_lang" ] && USAGE && exit 1
	;;
    -k|--client-kernel-pkg)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
	  client_kernel_pkg="$1"
	  shift
        fi
	[ -z "$client_kernel_pkg" ] && USAGE && exit 1
	;;
    -e|--extra-client-kernel-pkg)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
	  extra_client_kernel_pkg="$extra_client_kernel_pkg $1"
	  shift
        fi
	[ -z "$extra_client_kernel_pkg" ] && USAGE && exit 1
	;;
    -s|--client-kernel-ver-from-server)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
	  client_kernel_ver_from_server="$1"
	  shift
        fi
	[ -z "$client_kernel_ver_from_server" ] && USAGE && exit 1
	;;
    -o|--console-output)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
          CONSOLE_OUTPUT="$1"
	  shift
        fi
	[ -z "$CONSOLE_OUTPUT" ] && USAGE && exit 1
	;;
    -i|--pxe-serial-output)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
          PXE_SERIAL_OUTPUT="$1"
	  shift
        fi
	[ -z "$PXE_SERIAL_OUTPUT" ] && USAGE && exit 1
	;;
    -c|--no-required-pkgs-check)
	required_pkgs_check="no"
	shift;;
    -a|--no-prompt-different-arch-pkgs)
	prompt_different_arch_pkgs="no"
	shift;;
    -p|--skip-client-kernel-install)
	install_client_kernel="no"
	shift;;
    -r|--only-show-required-pkgs)
        only_show_required_pkgs="yes"
	shift;;
    -d|--dev-for-client-is-ready)
        dev_for_client_is_ready="yes"
	shift;;
    -v|--verbose)
	verbose="on"
	verbose_opt="-v"
        shift ;;
    -*) echo "${0}: ${1}: invalid option" >&2
        USAGE >& 2
        exit 2 ;;
    *)  break ;;
  esac
done

#
check_if_root

#
ask_and_load_lang_set $specified_lang

#
if [ "$install_client_kernel" = "yes" -a \
     "$only_show_required_pkgs" = "no" ]; then
  check_input_kernel_param
fi

# get the distribution name and type: OS_Version and OS_type
check_distribution_name
echo "$msg_delimiter_star_line"
echo "$msg_OS_version: $FULL_OS_Version"


# 0.
if [ "$required_pkgs_check" = "yes" ]; then
  check_required_pkgs
fi

# 1.
if [ "$prompt_different_arch_pkgs" = "yes" ]; then
  check_installed_pkgs_arch
fi

# 2. Prepare some config to avoid some delay when installing packages. If user already install them, we will overwrite those setting when running drblpush.
if [ "$OS_type" = "DBN" ]; then
  # We need to preconfig some packages, such as dhcp3-server, tftpd-hpa, nis so
  # it will not ask user to config. We will configure later in drblpush.
  deb-preconf-drbl
fi

# 3.
[ ! -d $pxecfg_pd ] && mkdir -p $pxecfg_pd
[ ! -d $drbl_common_root ] && mkdir -p $drbl_common_root
[ ! -d $drblroot ] && mkdir -p -m 700 $drblroot
[ ! -d $drbl_pkgdir ] && mkdir -p $drbl_pkgdir

# do some tunning.
if [ "$OS_type" = "DBN" ]; then
  check_debian_dev_for_client
fi

# 4.
if [ "$install_client_kernel" = "yes" ]; then
  install_kernel_for_client_func
else
  # the kernel for clients existed
  client_krn_mode="existed"
  echo "[-p|--skip-client-kernel-install] is assigned, assume the kernel for client already existed! Skip installing kernel for client!"
fi

# 5.
# Before running this function, we first check if memtest86+ and pxelinux exist in the drbl package repository ($memtest86_file and $pxelinux_file)
if [ ! -e "$memtest86_file" ]; then
  drbl-prepare-memtest
fi
if [ ! -e "$pxelinux_file" ]; then
  drbl-prepare-pxelinux
fi

# 6.
echo "$msg_delimiter_star_line"
[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
echo "$msg_create_files_for_PXELINUX"
[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
if [ -n "$CONSOLE_OUTPUT" ]; then
 console_opt1="--console-output"
 console_opt2="$CONSOLE_OUTPUT"
fi
if [ -n "$PXE_SERIAL_OUTPUT" ]; then
 pxe_serial_opt1="--pxe-serial-output"
 pxe_serial_opt2="$PXE_SERIAL_OUTPUT"
fi
prepare-files-for-PXE-client -l $lang $console_opt1 "$console_opt2" $pxe_serial_opt1 "$pxe_serial_opt2"

# 7.
# make the nbi/pxe files
echo "$msg_delimiter_star_line"
case "$client_krn_mode" in
   "from_pkg")
      if [ -n "$(file $client_kernel_pkg | grep -i RPM)" ]; then
        client_kernel_ver="$(rpm -qpl $client_kernel_pkg | grep -E "^/boot/vmlinuz-" | sed -e "s|/boot/vmlinuz-||g")"
        if [ -z "$client_kernel_ver" ]; then
          # For OpenSuSE 11.1, kernel-default or kernel-pae, there is no vmlinuz-* file, try another method, i.e. use the path:
          # /lib/modules/2.6.27.7-9-default/...
          client_kernel_ver="$(rpm -qpl $client_kernel_pkg | awk -F" " '{print $NF}' | awk -F"/" '/\/lib\/modules/ {print $4}' | sort | uniq)"
        fi
      elif [ -n "$(file $client_kernel_pkg | grep -i "Debian binary package")" ]; then
	client_kernel_ver="$(dpkg --contents $client_kernel_pkg | awk -F" " '{print $NF}' | grep -E "./boot/vmlinuz-" | sed -e "s|\./boot/vmlinuz-||g")"
        if [ -z "$client_kernel_ver" ]; then
          # For deb linux-ubuntu-modules-* or linux-restricted-modules-*, there is no vmlinuz-* file, try another method, i.e. use the path:
          # ./lib/modules/2.6.22-14-386/...
          client_kernel_ver="$(dpkg --contents $client_kernel_pkg | awk -F" " '{print $NF}' | awk -F"/" '/\.\/lib\/modules/ {print $4}' | sort | uniq)"
        fi
      fi
      drbl-gen-pxe-nbi -l $lang -k $client_kernel_ver
      #
      if [ -x /etc/init.d/linux-restricted-modules-common ]; then
	# Create mount point $drbl_common_root/lib/modules/$KERNEL_VER/volatile so that in Ubuntu with linux-restricted-modules, linux-restricted-modules-common service (which we will modify in drblpush so that it will use tmpfs otherwise it is Read-Only NFS filesystem) will need this.
        mkdir -p $drbl_common_root/lib/modules/$client_kernel_ver/volatile
      fi
      ;;
   "from_server")
      drbl-gen-pxe-nbi -l $lang -k $client_kernel_ver_from_server
      ;;
   "existed")
      # not from server, not from pkg, kernel must already exists for client
      drbl-gen-pxe-nbi -l $lang
      ;;
esac

echo "$msg_delimiter_star_line"
echo "$msg_Done!"
