#!/bin/bash
#===================================================#
# Make One Linux -- mkkmodule script                #
#  (C)2005 Keicho Kondo <dgel@users.sourceforge.jp> #
#===================================================#
# This script generates kernel module.
#
# This file may be copied under the terms of the GNU Public License
# http://www.gnu.org/licenses/gpl.html
#
# To save time the script will look for a relevant Module.symvers in:
#   /lib/modules
#   kernel binary rpm
#

## === Start === ##
echo "<-- Starting mkkmodule script -->"


## === Define === ##
BUILD_ROOT="${0%/*}"
CURRENT_DIR="${PWD}"
LOG=${CURRENT_DIR}/log-`date +%Y%m%d-%H%M%S`


## === Read module infomation file === ##
echo "Reading module infomation file..."
#
# * environment variable
#
#   - MOD_NAME       : module name
#   - MOD_COMPOPT    : compile options
#   - MOD_SRCDIR     : module source directory
#   - MOD_PATCH      : patch files
#
if [ -f "${CURRENT_DIR}/mkkmodule.conf" ]
then
	. ${CURRENT_DIR}/mkkmodule.conf
elif [ -f "${BUILD_ROOT}/mkkmodule.conf" ]
then
	. ${BUILD_ROOT}/mkkmodule.conf
else
	echo "Cannot find mkkmodule.conf file.  Giving up."
	exit 1
fi


## === Check kernel information === ##
echo "Checking kernel information..."
#
# * function
#
#   - checkchar <char1> <char2>   : If <char1> in <char2>, return 0, or return 1.
#       
# * environment variable
#
#   - RPM_TOPDIR     : RPM build top directory.
#   - KERNEL_SRCDIR  : kernel source directory.
#   - TYPE           : kernel type, rpm package or else.
#   - MAJORVERSION   : kernel major version.
#   - PATCHLEVEL     : kernel patchlevel.
#   - SUBLEVEL       : kernel sublevel.
#   - VERSION        : <majorversion>.<patchlevel>.<sublevel>
#   - RELEASE        : kernel extra version.
#   - CPU            : cpu architecture
#   - CONFIG         : kernel config file path (.config)
#   - MODSYMVER      : Module.symvers file path
# 

## Function
# check charactor
checkchar()
{
	case "$1" in
	*$2*) return 0 ;;
	*) return 1 ;;
	esac
}

# get rpm topdir
getrpmtopdir()
{
	RPM_MACROS="$1"
	RPM_TOPDIR=`awk '$1=="%_topdir" {print $2}' "$RPM_MACROS"`
	while checkchar "$RPM_TOPDIR" "%"
	do
		TMP="${RPM_TOPDIR##*\%}"
		RPM_TOPDIR_TMP="${TMP#*/}"
		RPM_PER_ALIAS="%`echo "${TMP%%/*}" | sed -e 's/{//' -e 's/}//'`"
		RPM_TOPDIR=`awk '$1=="'"$RPM_PER_ALIAS"'" {print $2}' "$RPM_MACROS"`/$RPM_TOPDIR_TMP
	done
	[ -d "$RPM_TOPDIR" ] || RPM_TOPDIR="/usr/src/redhat"
}

# check kernel source path
chkkernelsource()
{
	FLAG=""
	[ -d "$KERNEL_SRCDIR/drivers" ]  || FLAG="on"
	[ -d "$KERNEL_SRCDIR/init" ]     || FLAG="on"
	[ -d "$KERNEL_SRCDIR/lib" ]      || FLAG="on"
	[ -d "$KERNEL_SRCDIR/scripts" ]  || FLAG="on"
	[ -d "$KERNEL_SRCDIR/usr" ]      || FLAG="on"
	[ -d "$KERNEL_SRCDIR/arch" ]     || FLAG="on"
	[ -d "$KERNEL_SRCDIR/fs" ]       || FLAG="on"
	[ -d "$KERNEL_SRCDIR/ipc" ]      || FLAG="on"
	[ -d "$KERNEL_SRCDIR/mm" ]       || FLAG="on"
	[ -d "$KERNEL_SRCDIR/security" ] || FLAG="on"
	[ -d "$KERNEL_SRCDIR/crypto" ]   || FLAG="on"
	[ -d "$KERNEL_SRCDIR/include" ]  || FLAG="on"
	[ -d "$KERNEL_SRCDIR/kernel" ]   || FLAG="on"
	[ -d "$KERNEL_SRCDIR/net" ]      || FLAG="on"
	[ -d "$KERNEL_SRCDIR/sound" ]    || FLAG="on"
}

# interactive source code check
getkernelsource()
{
	echo "This script cannot find kernel source code."
	echo -n "Please enter your kernel source code path : "
	read KERNEL_SRCDIR
	chkkernelsource
	if [ -n "$FLAG" ]
	then
		echo "This is not a kernel source code path. failed."
		exit 1
	fi
}


## Look for kernel
# Search rpm topdir
echo -n "	Search rpm topdir..."
if (which rpm >/dev/null 2>&1)
then
	RPM_TOPDIR="/usr/src/redhat"
	if [ -f "$HOME/.rpmmacros" ]
	then
		getrpmtopdir "$HOME/.rpmmacros"
	elif [ -f "/usr/lib/rpm/macros" ]
	then
		getrpmtopdir "/usr/lib/rpm/macros"
	fi
fi
echo "			${RPM_TOPDIR}"

# Search for kernel source directory
echo -n "	Search for kernel source directory..."
KERNEL_SRCDIR=""
TYPE=""
if [ -f "${RPM_TOPDIR}/SOURCES/linux-"*".tar."*"" -a -f "${RPM_TOPDIR}/SPECS/kernel-"*".spec" ]
then
	echo "	${RPM_TOPDIR}"
	rpmbuild -bp --target=noarch ${RPM_TOPDIR}/SPECS/kernel-*.spec >/dev/null 2>&1
	if [ $? -ne 0 ]
	then
	echo " failed.  Giving up."
		exit 1
	fi
	KERNEL_SRCDIR="$RPM_TOPDIR/BUILD/kernel-"*"/linux-"*""
elif [ -d "/usr/src/linux" ]
then
	echo "	/usr/src/linux"
	KERNEL_SRCDIR="/usr/src/linux"
	chkkernelsource
	if [ -n "$FLAG" ]
	then
		getkernelsource
	fi
else
	echo ""
	getkernelsource
fi

# check kernel type
echo -n "	Check kernel type..."
case "$KERNEL_SRCDIR" in
*BUILD/kernel-*/linux-*)
	TYPE="rpm" ;;
*)
	TYPE=""
	mkdir -p ${CURRENT_DIR}/linux
	cp -a ${KERNEL_SRCDIR}/* ${KERNEL_SRCDIR}/.[^.]* ${CURRENT_DIR}/linux/
	KERNEL_SRCDIR="${CURRENT_DIR}/linux" ;;
esac
[ -n "${TYPE}" ] && echo "			${TYPE}" || echo "			original"


# Detect kernel version
echo -n "	Detect kernel version..."
MAJORVERSION=`awk '$1=="VERSION" {print $3}' ${KERNEL_SRCDIR}/Makefile`
PATCHLEVEL=`awk '$1=="PATCHLEVEL" {print $3}' ${KERNEL_SRCDIR}/Makefile`
SUBLEVEL=`awk '$1=="SUBLEVEL" {print $3}' ${KERNEL_SRCDIR}/Makefile`
EXTRAVERSION=`awk '$1=="EXTRAVERSION" {print $3}' ${KERNEL_SRCDIR}/Makefile`

VERSION="${MAJORVERSION}.${PATCHLEVEL}.${SUBLEVEL}"
if [ "$TYPE" = "rpm" ]
then
	RELEASE=$(uname -r | sed -e s/$VERSION//)
	for f in $(find ${KERNEL_SRCDIR}/configs -name kernel*.config | sed -e 's/.*'${VERSION}'-//' -e 's/\.config//' | awk -F'-' '{print $2}' | sort | uniq)
	do
		[ -z "$f" ] && continue
		case "${RELEASE}" in
		*$f)
			SUFF="$f" ;;
		esac
	done
	CPU=$(uname -p)
	CONFIG="${KERNEL_SRCDIR}/configs/kernel-${VERSION}-${CPU}${SUFF}.config"
	MODSYMVER="/lib/modules/${VERSION}${RELEASE}/build/Module.symvers"
else
	RELEASE="${EXTRAVERSION}"
	CPU=""
	CONFIG="${KERNEL_SRCDIR}/.config"
	MODSYMVER="${KERNEL_SRCDIR}/Module.symvers"
fi
echo "		${VERSION}${RELEASE}"


## === Other checks === ###
echo "Other checks..."
# check kernel config file
echo -n "	Check kernel config file..."
if [ ! -f ${CONFIG} ]
then
	echo "Cannot find kernel config file in ${CONFIG%/*}.   Giving up."
	exit 1
fi
echo "		ok"
# check Module.symvers
echo -n "	Check Module.symvers..."
if [ ! -f "${MODSYMVER}" ]
then
	echo "Cannot find kernel config file in ${MODSYMVER%/*}.   Giving up."
	exit 1
fi
echo "			ok"


## === Start log service === ##
echo >> $LOG
echo "Log started at `date`" >> $LOG
echo >> $LOG


## === Patch kernel source === ##
echo "Patch Section..."
pushd ${KERNEL_SRCDIR} >> $LOG 2>&1
if [ -n "${MOD_PATCH}" ]
then
	for patch in ${MOD_PATCH}
	do
		if [ -f "${CURRENT_DIR}/${patch}" ]
		then
			patch -p1 < "${CURRENT_DIR}/${patch}" >> $LOG 2>&1
			if [ $? -ne 0 ]
			then
				echo "Cannot apply patch file.  Giving up."
				exit 1
			fi
		elif [ -f "${BUILD_ROOT}/${patch}" ]
		then
			patch -p1 < "${BUILD_ROOT}/${patch}" >> $LOG 2>&1
			if [ $? -ne 0 ]
			then
				echo "Cannot apply patch file.  Giving up."
				exit 1
			fi
		fi
	done
fi
popd >> $LOG 2>&1

## === Compile kernel module === ##
echo "Compile Section..."
pushd ${KERNEL_SRCDIR} >> $LOG 2>&1
sed -e "s/^EXTRAVERSION.*/EXTRAVERSION = ${RELEASE}/" Makefile > Makefile.tmp && mv -f Makefile.tmp Makefile

[ -z "${TYPE}" ] && cp -f Module.symvers .config ../

# Clear out any existing build products
echo -n "	make distclean..."
make V=0 distclean >> $LOG 2>&1
if [ $? -ne 0 ]
then
	echo " failed.  Giving up."
	exit 1
fi
echo "			done"

# Copy kernel config file
[ "${TYPE}" = "rpm" ] && cp -f $CONFIG .config
[ -z "${TYPE}" ] && mv -f ../.config .

# Edit kernel config file
for opt in $MOD_COMPOPT
do
	echo $opt >> ${KERNEL_SRCDIR}/.config
done

# Update the build config file, answering "no" to any new questions.
echo -n "	make oldconfig..."
yes n | make V=0 oldconfig >> $LOG 2>&1
if [ $? -ne 0 ]
then
	echo " failed.  Giving up."
	exit 1
fi
echo "			done"

# prepare-all
make V=0 prepare-all >> $LOG 2>&1

# Copy Module.symvers
[ "${TYPE}" = "rpm" ] && cp -f $MODSYMVER .
[ -z "${TYPE}" ] && mv -f ../Module.symvers .

# Finally compile the module
echo -n "	make modules..."
make modules V=0 M="$MOD_SRCDIR" >> $LOG 2>&1
if [ $? -ne 0 ]
then
echo " failed.  Giving up."
	exit 1
fi
echo "				done"

popd >> $LOG 2>&1


## === Get kernel module === ##
cp -a ${KERNEL_SRCDIR}/${MOD_SRCDIR}/${MOD_NAME}.ko ${CURRENT_DIR}/


## === Finish === ##
if [ "${TYPE}" = "rpm" ]
then
	rpmbuild --clean ${RPM_TOPDIR}/SPECS/kernel-*.spec >> $LOG 2>&1
elif [ -z "${TYPE}" ]
then
	rm -rf "${CURRENT_DIR}/linux"
fi

echo >> $LOG
echo "Log ends at `date`" >> $LOG
echo >> $LOG

echo "<-- Finished -->"

