#!/bin/bash
# Author: Steven Shiau <steven _at_ clonezilla org>, Ceasar Sun <ceasar _at_ nchc org tw>
# License: GPL 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#

# Load DRBL setting and functions
# Setting
# Source function library.
[ -f /etc/rc.d/init.d/functions ] && . /etc/rc.d/init.d/functions

# Source DRBL setting
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 "-k, --client-kernel-pkg KERNEL_PKG: specify the KERNEL_PKG (rpm or deb) which you want DRBL client to use."
   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 /usr/lib/modules/KERNEL_VER..."
   echo "-c, --just-cp-files: just install files for client, do not create kernel tag for client and do not run some script. Usually this option is useed to install linux-restricted-modules in ubuntu."
   echo "-v, --verbose:       prints verbose information"
}

# default settings
just_cp_files="no"
depmod_stderr="/dev/null"

# Parse command-line options
while [ $# -gt 0 ]; do
  case "$1" in
    -l|--language)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
	  specified_lang="$1"
	  shift
        fi
	;;
    -k|--client-kernel-pkg)
	shift
        if [ -z "$(echo $1 |grep ^-.)" ]; then
          # skip the -xx option, in case 
	  client_kernel_pkg="$1"
	  shift
        fi
	;;
    -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
	;;
    -c|--just-cp-files)
	shift; just_cp_files="yes"
	;;
    -v|--verbose)
	verbose="on"
	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 [ "$verbose" = "on" ]; then
  # The default output for depmod is surpressed, now turn it on
  depmod_stderr="/dev/stderr"
fi

# parse the input kernel package
if [ -n "$client_kernel_pkg" ]; then
   if [ -n "$(file -Ls $client_kernel_pkg | grep -i RPM)" ]; then
     mode="rpm"
   elif [ -n "$(file -Ls $client_kernel_pkg | grep -i "Debian binary package")" ]; then
     mode="deb"
   fi
elif [ -n "$client_kernel_ver_from_server" -a \
       -f /boot/vmlinuz-$client_kernel_ver_from_server -a \
       -d /usr/lib/modules/$client_kernel_ver_from_server ]; then
   mode="local"
   client_kernel_ver="$client_kernel_ver_from_server"
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
case "$mode" in
   rpm)
    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:
      # /usr/lib/modules/2.6.27.7-9-default/...
      client_kernel_ver="$(rpm -qpl $client_kernel_pkg | grep -E "/usr/lib/modules/[[:print:]]+" | awk -F" " '{print $NF}' | awk -F"/" '/\/usr\/lib\/modules/ {print $4}' | sort | uniq)"
    fi
    # we might need /tftpboot/node_root/var/lock/rpm/ for locking in some dist (like CentOS 4.4).
    mkdir -p $drbl_common_root/var/lock/rpm/
    echo "Installing $client_kernel_pkg for clients... "
    echo "$msg_install_kernel_might_take_several_minutes "
    # We do not care about those files put into $drbl_common_root/usr/
    rpm -ivh --root $drbl_common_root/ --force --nodeps --noscripts $client_kernel_pkg
    rc=$?
    echo "$msg_done!"
    ;;
   deb)
    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-modules-*, there is no vmlinuz-* file, try another method, i.e. from dpkg-deb -f:
      client_kernel_ver="$(dpkg-deb -f $client_kernel_pkg Package | sed 's/^linux-modules-//')"
    fi
    echo "Installing $client_kernel_pkg for clients... "
    echo "$msg_install_kernel_might_take_several_minutes "

    # Check if "^./lib/modules/" exists. If so, it's old format (Debian <= 12)
    cvrt_flag="no"
    if [ -n "$(dpkg -c $client_kernel_pkg | awk '{print $NF}' | grep -Ew "^\.\/lib\/modules\/")" ]; then
      cvrt_flag="yes"
    fi

    dpkg-deb --extract $client_kernel_pkg $drbl_common_root
    rc=$?
    # 2025/Sep/21
    # "dpkg-deb --extract" command will overwrite the linked file $drbl_common_root/lib file even when it already exists.
    # For Debian <= 12, linux-image will have files under /lib/modules/, e.g.:
    # linux-image-6.1.0-39-amd64:
    # /lib/modules/6.1.0-39-amd64/kernel/arch/x86/crypto/aegis128-aesni.ko
    # /lib/modules/6.1.0-39-amd64/kernel/arch/x86/crypto/aesni-intel.ko
    # /lib/modules/6.1.0-39-amd64/kernel/arch/x86/crypto/blowfish-x86_64.ko
    # While for newer Debian/Ubuntu, they are like:
    # linux-image-6.12.38+deb13-amd64:
    # /usr/lib/modules/6.12.38+deb13-amd64/kernel/arch/x86/crypto/aegis128-aesni.ko.xz
    # /usr/lib/modules/6.12.38+deb13-amd64/kernel/arch/x86/crypto/aesni-intel.ko.xz
    # /usr/lib/modules/6.12.38+deb13-amd64/kernel/arch/x86/crypto/blowfish-x86_64.ko.xz
    # Hence
    # If $drbl_common_root/lib is a dir, not linked file, and if dir $drbl_common_root/lib/modules/ exists
    # linux-image is still in the old path /lib/modules/*
    # We move $drbl_common_root/lib/moduels to drbl_common_root/usr/lib/modules/, remove linked file $drbl_common_root/lib
    # and create a link file $drbl_common_root/lib to drbl_common_root/usr/lib
    if [ "$cvrt_flag" = "yes" ]; then
      if [ -d "$drbl_common_root/lib" ]; then
        if [ -d "$drbl_common_root/lib/modules" ]; then
          echo -n "Converting the old path of kernel modules $drbl_common_root/lib to $drbl_common_root/usr/lib "
          mkdir -p $drbl_common_root/usr/lib/modules/  # in case modules dir does not exist.
          rsync -a $drbl_common_root/lib/modules/* $drbl_common_root/usr/lib/modules/
        fi
	rm -rf $drbl_common_root/lib/*
        rmdir $drbl_common_root/lib/
        (cd $drbl_common_root; ln -fs usr/lib lib)
      fi
    fi
    echo "$msg_done!"
    ;;
  "local")
    echo "Installing kernel $client_kernel_ver for clients... "
    echo -n "$msg_install_kernel_might_take_several_minutes"
    echo -n "."
    mkdir -p $drbl_common_root/boot/ $drbl_common_root/usr/lib/modules/
    echo -n "."
    rsync -a /boot/*$client_kernel_ver $drbl_common_root/boot/
    echo -n "."
    rsync -a /usr/lib/modules/$client_kernel_ver $drbl_common_root/usr/lib/modules/
    echo "$msg_done!"
    rc=0
    ;;
esac

if [ "$rc" -eq 1 ]; then
  [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
  echo "Failed to install the kernel $client_kernel_pkg!"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  exit 1
fi

if [ ! "$just_cp_files" = "yes" ]; then 
  # For FC 22 or later, rpm -ivh doesn't put /boot/System.map-$client_kernel_ver with --noscripts
  # Therefore we copy vmlinuz, System.map-$client_kernel_ver, config-$client_kernel_ver
  # This is only required for Fedora rpm. Ubuntu's linux-modules deb contains System.map-$client_kernel_ver
  if [ ! -e "$drbl_common_root/boot/System.map-$client_kernel_ver" -a \
	 -n "$(ls $client_kernel_pkg | grep -iE "^.*.rpm")" ]; then
    KRC=0
    echo "Preparing client's /boot/System.map-$client_kernel_ver..."
    _ksystem_map_realpath="$(LC_ALL=C rpm -qpl $client_kernel_pkg | grep -E 'System.map' | grep -vE '^/boot/')"
    _ksystem_map_bootpath="$(LC_ALL=C rpm -qpl $client_kernel_pkg | grep -E '^/boot/System.map' )"
    if [ -e "$drbl_common_root/$_ksystem_map_realpath" \
         -a "$_ksystem_map_bootpath" = "/boot/System.map-$client_kernel_ver" ]
    then 
      cp -a $drbl_common_root/$_ksystem_map_realpath $drbl_common_root/$_ksystem_map_bootpath
    else
      KRC=1
    fi

    echo "Preparing client's /boot/vmlinuz-$client_kernel_ver..."
    _kvmlinuz_realpath="$(LC_ALL=C rpm -qpl $client_kernel_pkg | grep -E 'vmlinuz$' | grep -vE '^/boot/')"
    _kvmlinuz_bootpath="$(LC_ALL=C rpm -qpl $client_kernel_pkg | grep -E '^/boot/vmlinuz' )"
    if [ -e "$drbl_common_root/$_kvmlinuz_realpath" \
	 -a "$_kvmlinuz_bootpath" = "/boot/vmlinuz-$client_kernel_ver" ]
    then 
      cp -a $drbl_common_root/$_kvmlinuz_realpath $drbl_common_root/$_kvmlinuz_bootpath
    else
      KRC=1
    fi

    echo "Preparing client's /boot/config-$client_kernel_ver ..."
    _kconfig_realpath="$(LC_ALL=C rpm -qpl $client_kernel_pkg | grep -E 'config$' | grep -vE '^/boot/')"
    _kconfig_bootpath="$(LC_ALL=C rpm -qpl $client_kernel_pkg | grep -E '^/boot/config' )"
    if [ -e "$drbl_common_root/$_kconfig_realpath" \
	 -a "$_kconfig_bootpath" = "/boot/config-$client_kernel_ver" ]
    then 
      cp -a $drbl_common_root/$_kconfig_realpath  $drbl_common_root/$_kconfig_bootpath
    else
      KRC=1
    fi

    # Output error messages about failing to prepare kernel for clients.
    if [ "$KRC" -ne 0 ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
      echo "Failed to prepare Linux kernel for DRBL clients in RedHat-like environment."
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      echo "$msg_program_stop!"
      exit 1
    fi
  fi
  # Need to create the files
  # $pxecfg_pd/kernel_version_in_initrd.txt
  # $pxecfg_pd/client_kernel_arch.txt
  # Only when we can find something like /tftpboot/node_root/boot/System.map-2.6.27.39-0.2-default we can find KARCH.
  if [ -e "$drbl_common_root/boot/System.map-$client_kernel_ver" ]; then
    # make a tag in the system for client's kernel arch.
    KARCH="$(LC_ALL=C drbl-check-kernel-cpu-arch --drbl-client $client_kernel_ver)"
    if [ -z "$KARCH" ]; then
      [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
      echo "Client CPU arch NOT found! The $pxecfg_pd/client_kernel_arch.txt will be empty!"
      [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
      echo -n "$msg_press_enter_to_continue"
      read 
    fi
    mkdir -p $pxecfg_pd/
    echo "$KARCH" > $pxecfg_pd/client_kernel_arch.txt
  fi

  # UGLY... find a better to strip the tail for MDK kernel
  # for MDK kernel-2.6.8.1.24mdk-1-1mdk, however the kernel tag is 2.6.8.1-24mdk
  # we need to strip the -1-1mdk
  client_kernel_ver="$(echo $client_kernel_ver | sed -e "s/-1-1mdk//g")"
fi

# Create modules.dep...
# we still neeed to build $drbl_common_root/boot/System.map-$client_kernel_ver for client if kernel is installed from package
if [ "$mode" != "local" ]; then
  if [ -e "$drbl_common_root/boot/System.map-$client_kernel_ver" ]; then
     echo -n "Generating modules.dep and map files for clients... "
     depmod -ae -b $drbl_common_root/ -F $drbl_common_root/boot/System.map-$client_kernel_ver $client_kernel_ver 2> $depmod_stderr
     echo "done!"
  else
     echo "$drbl_common_root/boot/System.map-$client_kernel_ver NOT found! Assume it's in another kernel package."
  fi
fi

# we stop here for kernel extra/module package 
[ "$just_cp_files" = "yes" ] && exit 0

# Firmwares. For Ubuntu, the firmwares come with kernel package, however, it's not true for other distributions. We can only try to use that from server
if [ -d "/usr/lib/firmware" ]; then
  echo "Preparing the kernel firmware for clients..."
  rsync -a /usr/lib/firmware $drbl_common_root/usr/lib/
fi

exit 0
