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

# Load DRBL setting and functions
DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/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
# Here we check both pxelinux.0 and gpxelinux.0. Since if older version of syslinux is installed (e.g. 3.7 in CentOS 5), no gpxelinux.0. However, if the user upgrade syslinux as 4.x after drblsrv-offline is run, drbl-prepare-pxelinux won't be run if not checking gpxelinux.0.
if [ ! -e "$pxelinux_file" -o ! -e "$gpxelinux_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!"
