#!/bin/sh
# $Id: update-kernel,v 1.9.2.7 2010/07/01 17:22:27 mikpe Exp $
# usage:
#	cd ${kernelsrcdir}
#	${perfctrsrcdir}/update-kernel

perfctr=
patch=
patchtype=
nopatch=
dryrun=
sym=
backup=
maybe_redhat=
maybe_suse=
VERSION=
PATCHLEVEL=
SUBLEVEL=
EXTRAVERSION=
kernel=

usage() {
    echo 'Usage: cd ${kernelsrcdir}; ${perfctrsrcdir}/update-kernel [options]'
    echo 'Available options:'
    echo '--patch=version force use of patch for particular kernel version'
    echo '-p version      ditto'
    echo '--nopatch       do not apply patch, only install new files'
    echo '-n              ditto'
    echo '--test          apply the patch but do not change any files'
    echo '-t              ditto'
    exit 1
}

get_perfctr_srcdir() {
    # XXX: use dirname instead?
    perfctr=`echo $0 | sed 's+/update-kernel$++'`
}

get_arch() {
    if [ -z "$ARCH" ]; then
	OLDARCH=`uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/`
	ARCH=`echo $OLDARCH | sed -e s/i386/x86/ -e s/x86_64/x86/ -e s/ppc.*/powerpc/`
    fi
}

get_options() {
    while [ $# -gt 0 ]; do
	case "$1" in
	  --patch=*)
	    patch=`echo "$1" | sed s/--patch=//`
	    ;;
	  -p)
	    shift
	    if [ $# -eq 0 ]; then
		echo Error: option "'-p'" requires an argument
		usage
	    fi
	    patch="$1"
	    ;;
	  --nopatch|-n)
	    nopatch=1
	    ;;
	  --test|-t)
	    dryrun="--dry-run"
	    ;;
	  --sym)
	    # not for general use
	    sym=1
	    ;;
	  --backup)
	    # not for general use
	    backup=1
	    ;;
	  *)
	    echo Error: unknown option "$1"
	    usage
	    ;;
	esac
	shift
    done
}

check_pwd_is_kernel_srcdir() {
    if [ ! -f drivers/Makefile -o \( ! -f "arch/$ARCH/kernel/Makefile" -a ! -f "arch/$OLDARCH/kernel/Makefile" \) ]; then
	echo "Error: `pwd` does not appear to be the top-level kernel source directory"
	usage
    fi
}

check_kernel_srcdir_is_clean() {
    if [ -f System.map -o -f .version ]; then
	echo 'Error: kernel source is not clean; please do a "make mrproper"'
	exit 1
    fi
}

get_makefile_variable() {
    eval `grep "^$1 *=" Makefile | head -n 1 | sed 's/ *= */=/'`
}

get_extra_version() {
    EXTRAVERSION=`grep "^EXTRAVERSION *=" Makefile | head -n 1 | sed 's/ *= */=/'`
    case "$EXTRAVERSION" in
    'EXTRAVERSION=-$(shell echo $(CONFIG_RELEASE)-$(CONFIG_CFGNAME))')
	maybe_suse=1
	EXTRAVERSION='EXTRAVERSION=-$(shell echo $(CONFIG_RELEASE))'
	;;
    esac
    MKF=`cat <<EOF
-include .config
$EXTRAVERSION
default:
	@echo EXTRAVERSION='\\$(EXTRAVERSION)'
EOF`
    eval `echo "$MKF" | make -f -`
}

get_kernel_version() {
    get_makefile_variable VERSION
    get_makefile_variable PATCHLEVEL
    get_makefile_variable SUBLEVEL
    get_extra_version
    case "$EXTRAVERSION" in
    *custom)
	maybe_redhat=1
	EXTRAVERSION=`echo $EXTRAVERSION | sed 's/\(.*\)custom/\1/'`
	;;
    esac
    kernel="$VERSION.$PATCHLEVEL.$SUBLEVEL$EXTRAVERSION"
}

check_given_patch() {
    if [ ! -f "$perfctr/patches/patch-kernel-$patch" ]; then
	alias=`grep ^$patch/ $perfctr/patches/aliases | awk -F/ '{ print $2 }'`
	if [ -n "$alias" -a -f "$perfctr/patches/patch-kernel-$alias" ]; then
	    patch="$alias"
	    echo Using patch "$alias" for kernel "$kernel"
	else
	    echo Error: no patch for user-specified version "$patch" found
	    echo See "$perfctr/patches/" for supported kernel versions
	    exit 1
	fi
    fi
    patchtype="(forced, kernel is $kernel)"
}

try_standard() {
    if [ -f "$perfctr/patches/patch-kernel-$kernel" ]; then
	patch="$kernel"
	echo Found patch for kernel "$kernel"
    else
	alias=`grep ^$kernel/ $perfctr/patches/aliases | awk -F/ '{ print $2 }'`
	if [ -n "$alias" -a -f "$perfctr/patches/patch-kernel-$alias" ]; then
	    patch="$alias"
	    echo Using patch "$alias" for kernel "$kernel"
	fi
    fi
}

try_redhat() {
    if [ -f /etc/redhat-release ]; then
	maybe_redhat=1
    fi
    if [ -n "$EXTRAVERSION" -a -n "$maybe_redhat" ]; then
	if [ -f "$perfctr/patches/patch-kernel-$kernel-redhat" ]; then
	    patch="$kernel-redhat"
	    echo Found patch for RedHat kernel "$kernel"
	else
	    alias=`grep ^$kernel-redhat $perfctr/patches/aliases | awk -F/ '{ print $2 }'`
	    if [ -n "$alias" -a -f "$perfctr/patches/patch-kernel-$alias" ]; then
		patch="$alias"
		echo Using patch "$alias" for RedHat kernel "$kernel"
	    fi
	fi
    fi
}

try_suse() {
    if [ -n "$EXTRAVERSION" -a -n "$maybe_suse" ]; then
	if [ -f "$perfctr/patches/patch-kernel-$kernel-suse" ]; then
	    patch="$kernel-suse"
	    echo Found patch for SuSE kernel "$kernel"
	else
	    alias=`grep ^$kernel-suse $perfctr/patches/aliases | awk -F/ '{ print $2 }'`
	    if [ -n "$alias" -a -f "$perfctr/patches/patch-kernel-$alias" ]; then
		patch="$alias"
		echo Using patch "$alias" for SuSE kernel "$kernel"
	    fi
	fi
    fi
}

deduce_patch() {
    try_standard
    if [ -z "$patch" ]; then
	try_redhat
    fi
    if [ -z "$patch" ]; then
	try_suse
    fi
    if [ -z "$patch" ]; then
	echo Error: no suitable patch found for kernel "$kernel"
	echo See "$perfctr/patches/" for supported kernel versions
	exit 1
    fi
    patchtype="(auto-detected)"
}

identify_patch() {
    if [ -n "$patch" ]; then
	check_given_patch
    else
	deduce_patch
    fi
}

apply_patch() {
    echo
    echo Applying kernel source patch for "$patch" "$patchtype"
    echo
    bVt=
    if [ "$backup" ]; then
	bVt="-bVt"
    fi
    patch $dryrun $bVt -p1 < "$perfctr/patches/patch-kernel-$patch"
}

install_file() {
    if [ -n "$sym" ]; then
	echo ln -sf ${perfctr}/$1/$2 $3/
	if [ -z "$dryrun" ]; then
	    ln -sf ${perfctr}/$1/$2 $3/
	fi
    else
	echo tar -C "${perfctr}/$1/" --exclude CVS -cf - $2 '|' tar -C $3/ -xf -
	if [ -z "$dryrun" ]; then
	    tar -C "${perfctr}/$1/" --exclude CVS -cf - $2 | tar -C $3/ -xf -
	fi
    fi
}

install_files() {
    echo
    echo Installing new kernel source files
    echo
    if [ -d arch/x86/include/asm ]; then
	install_file linux/include/asm-x86 perfctr.h arch/x86/include/asm
    elif [ -d include/asm-x86 ]; then
	install_file linux/include/asm-x86 perfctr.h include/asm-x86
    else
	install_file linux/include/asm-x86 perfctr.h include/asm-i386
	install_file linux/include/asm-x86 perfctr.h include/asm-x86_64
    fi
    if [ -d arch/powerpc/include/asm ]; then
	install_file linux/include/asm-powerpc perfctr.h arch/powerpc/include/asm
    elif [ -d include/asm-powerpc ]; then
	install_file linux/include/asm-powerpc perfctr.h include/asm-powerpc
    else
	install_file linux/include/asm-powerpc perfctr.h include/asm-ppc
    fi
    if [ -d arch/arm/include/asm ]; then
	install_file linux/include/asm-arm perfctr.h arch/arm/include/asm
    else
	install_file linux/include/asm-arm perfctr.h include/asm-arm
    fi
    install_file linux/include/linux perfctr.h include/linux
    install_file linux/drivers perfctr drivers
}

get_perfctr_srcdir
get_arch
get_options "$@"
check_pwd_is_kernel_srcdir
check_kernel_srcdir_is_clean
get_kernel_version
if [ -z "$nopatch" ]; then
    identify_patch
    apply_patch
fi
install_files
