#!/bin/bash
# Copyright (C) 2004, 2013, 2015 Vitaly Lipatov <lav@etersoft.ru>
# Copyright (C) 2005-2008 Anatoly Kitaykin <cetus@newmail.ru>
# Copyright (C) 2008-2009 Vladimir V. Kamarzin <vvk@altlinux.org>
# Copyright (C) 2008-2009 Michael Shigorin <mike@altlinux.org>
# Copyright (C) 2008 Konstantin Baev <kipruss@altlinux.org>
# Copyright (C) 2010 Dmitry Kulik <lnkvisitor@altlinux.org>
#
# Update kernel with modules
#
# This file 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 of the License, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

. shell-error
. shell-args

show_help()
{
        cat <<EOF
Usage: $PROG [options]
Valid options are:
	-a, --all         install all kernel modules
	-i, --interactive interactive modules install
	-f, -y, --force   force kernel upgrade
	-t, --type        install kernel with specified flavour (ovz-smp, std-def, etc)
	-r, --release     install kernel with specified release (alt10, alt22, etc)
	-n, --dry-run     download packages, but don't install (--download-only)
	-h, --help        show this text and exit
EOF
exit 1
}

# module_install will either instal module $1 or update modules_to_install
# variable with this module to install it in one transaction with kernel
module_install() {
    module=$1

    if [ "$interactive" = 1 ]; then
	echo -n "Try to install or update module $module [y]/n? "
	read
	case "$REPLY" in
		n|no|N|No|NO|0) return 0
			;;
		*)
			;;
	esac
	$SUDO apt-get install -o APT::Install::VirtualVersion=true -o APT::Install::Virtual=true -y $dryrun "$module"
    else
	export modules_to_install="$modules_to_install $1"
    fi
}

#parse command line options
TEMP=`getopt -n $PROG -o a,i,f,y,t:,r:,n,h -l interactive,all,force,type:,release:,dry-run,help -- "$@"` || show_help
eval set -- "$TEMP"

while :; do
        case "$1" in
                --) shift; break
                        ;;
                -a|--all) all=1
                        ;;
                -i|--interactive) interactive=1
                        ;;
                -f|-y|--force) force=1
                        ;;
		-t|--type) shift ; kernel_flavour="$1"
			;;
		-r|--release) shift ; release="$1"
			;;
		-n|--dry-run) dryrun="--download-only"
			;;
                -h|--help) show_help
        esac
	shift
done

if [ -n "$interactive" -a -n "$force" ]; then
	show_usage '--force and --interactive are mutually exclusive options.'
fi

###################################################################
# update kernel and modules

# get running kernel version
current_kernel_package=$(rpmquery -qf /lib/modules/$(uname -r)/kernel 2>/dev/null)
if [ -n "$current_kernel_package" ] ; then
    current_kernel_pkgname=$(rpmquery --queryformat "%{NAME}-%{SERIAL}:%{VERSION}-%{RELEASE}\n" -q $current_kernel_package)
    echo $current_kernel_pkgname | grep -q "(none)" && current_kernel_pkgname=$(rpmquery --queryformat "%{NAME}-%{VERSION}-%{RELEASE}\n" -q $current_kernel_package)
else
    current_kernel_pkgname="UNKNOWN"
fi
echo "Running kernel version: $current_kernel_pkgname"


echo "Checking for available kernel packages..."
# get list of all available kernel packages
KERNEL_PKGS="$(apt-cache pkgnames kernel-image)"

# set kernel flavour. if not defined with -t option, use current
current_kernel_flavour="$(uname -r)"
current_kernel_flavour="${current_kernel_flavour#*-}"
current_kernel_flavour="${current_kernel_flavour%-*}"
kernel_flavour="${kernel_flavour:-$current_kernel_flavour}"

# check that we have at least one kernel with defined kernel_flavour
num_available_kernels="$(echo "$KERNEL_PKGS" | grep -c "$kernel_flavour")"
[ "$num_available_kernels" != 0 ] || fatal "There are no available kernels with kernel flavour $kernel_flavour"

# check that we have at least one kernel with defined kernel package release
[ -z "$release" ] || {
	num_available_kernels="$(echo "$KERNEL_PKGS" | grep -c "$release")"
	[ "$num_available_kernels" != 0 ] || fatal "There are no available kernels with package release $release"
}

# define how we must select available packages with needed flavour/release
[ -z "$release" ] && pgkgrep="kernel-image-$kernel_flavour#" || pgkgrep="kernel-image-$kernel_flavour#.*$release$"

# get the maximum available kernel package version
kmaxver=
while read version
do
	comparever="$(rpmevrcmp "$kmaxver" "$version")"
	[ "$comparever" -lt 0 ] && kmaxver="$version" ||:
done <<<"$(echo "$KERNEL_PKGS" | grep "$pgkgrep" | sed -e "s,^kernel-image-$kernel_flavour#,,g")"

# check if the current kernel is up-to-date
if [ -z "$force" ] && [ "$current_kernel_pkgname" = "kernel-image-$kernel_flavour-$kmaxver" ] ; then
	echo "Latest kernel $kernel_flavour-$kmaxver is already running on your system."
	echo "No upgrade is needed. Use -f for force."
	exit 0
fi

# check if selected kernel is already installed
if [ -z "$force" ] && rpm -q "kernel-image-$kernel_flavour-$kmaxver" >/dev/null ; then
	echo "Latest kernel $kernel_flavour-$kmaxver is already installed on your system."
	echo "No upgrade is needed. Use -f for force."
	exit 0
fi

# ask user
echo -n "Try to install new kernel kernel-image-$kernel_flavour-$kmaxver and update its modules [y]/n? "
[ "$force" = 1 ] && echo "yes" || {
	read
	case "$REPLY" in
		n|no|N|No|NO|0) exit 0
			;;
		*)
			;;
	esac
}

# is there kernel-image-$kernel_flavour already installed or not?
# needed for proper modules selection
rpm -q kernel-image-"$kernel_flavour" &>/dev/null && modules_kernel_flavour="$kernel_flavour" \
	|| modules_kernel_flavour="$current_kernel_flavour"

# use sudo(1) if running as unprivileged user
[ "$UID" = "0" ] && SUDO= || SUDO=sudo

# install kernel iff we are working in interactive mode
if [ -n "$interactive" ]; then
	$SUDO apt-get install -y $dryrun "kernel-image-$kernel_flavour#$kmaxver" \
		|| fatal "failed to install kernel-image-$kernel_flavour-$kmaxver"
fi

###################################################################
# update modules

[ -z "$interactive" ] ||
message "Updating modules for kernel: $kernel_flavour-$kmaxver"

# get list of all available modules
ALLMODULES="$(apt-cache whatdepends kernel-image-$kernel_flavour#$kmaxver | grep "kernel-modules-.*-$kernel_flavour" | sed -re 's/-[^-]*-[^-]*$//'  | sort -u)"

# install modules or set modules_to_install variable
modules_to_install=
for module in $ALLMODULES; do
	module_pkgname="${module%%#*}"
	module_pkgname="${module_pkgname%-$kernel_flavour}"
	if rpm -q $module_pkgname-"$modules_kernel_flavour" &>/dev/null; then
		message "$module_pkgname is installed, trying to update..."
	elif [ "$all" = 1 ]; then
		message "$module_pkgname is not istalled, trying to install..."
	else
		continue
	fi

	# remove duplicates
	module=$(echo "$ALLMODULES" | grep "^[[:space:]]*${module_pkgname}-${kernel_flavour}#" | sort -r -u -V | head -1)

	module_install "$module"
done

# now install both kernel and modules iff we are not interactive
# (in wich case all of them already installed)
if [ -z "$interactive" ]; then
	$SUDO apt-get install -y $dryrun "kernel-image-$kernel_flavour#$kmaxver" $modules_to_install \
		|| fatal "failed to install kernel-image-$kernel_flavour-$kmaxver with modules"
fi

# try to run x11setupdrv, x11presetdrv
# if might need that (e.g. for nvidia_glx updates)
# NB: x11setupdrv seems deprecated in Sisyphus as of Oct 2009
X11SETUPDRV=/usr/bin/x11setupdrv
X11PRESETDRV=/usr/sbin/x11presetdrv
XORG=/usr/bin/Xorg

[ ! -x "$XORG" ] || \
case "$ALLMODULES" in
	*drm*|*fglrx*|*nvidia*)
		[ -x "$X11SETUPDRV" ] \
			&& $SUDO "$X11SETUPDRV"
		[ -x "$X11PRESETDRV" ] \
			&& $SUDO "$X11PRESETDRV" \
			|| message "You might need to run x11presetdrv, video drivers updated but it's missing"
		$SUDO ldconfig
		;;
esac


###################################################################
# update headers

for package in kernel-headers kernel-headers-modules; do
	if rpm -q $package-"$kernel_flavour" &>/dev/null; then
		message "$package is installed, trying to update..."
		$SUDO apt-get install -y $dryrun "$package-$kernel_flavour"
	fi
done
