#!/bin/bash
#
#  Dynamic Kernel Module Support (DKMS) <dkms-devel@dell.com>
#  Copyright (C) 2003-2008 Dell, Inc.
#  by Gary Lerhaupt, Matt Domsch, & Mario Limonciello
#  Copyright (C) 2012 by Darik Horn <dajhorn@vanadac.com>
#
#    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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

shopt -s extglob

# All of the variables we will accept from dkms.conf.
# Does not include directives
readonly dkms_conf_variables="CLEAN REMAKE_INITRD remake_initrd PACKAGE_NAME
   PACKAGE_VERSION POST_ADD POST_BUILD POST_INSTALL POST_REMOVE PRE_BUILD
   PRE_INSTALL BUILD_DEPENDS BUILD_EXCLUSIVE_KERNEL BUILD_EXCLUSIVE_ARCH
   build_exclude OBSOLETE_BY MAKE MAKE_MATCH MODULES_CONF
   modules_conf_array PATCH PATCH_MATCH patch_array BUILT_MODULE_NAME
   built_module_name BUILT_MODULE_LOCATION built_module_location
   DEST_MODULE_NAME dest_module_name MODULES_CONF_OBSOLETES
   DEST_MODULE_LOCATION dest_module_location
   modules_conf_obsoletes MODULES_CONF_ALIAS_TYPE
   modules_conf_alias_type STRIP strip MODULES_CONF_OBSOLETE_ONLY
   modules_conf_obsolete_only AUTOINSTALL NO_WEAK_MODULES
   SIGN_TOOL"

# Some important regular expressions.  Requires bash 3 or above.
# Any poor souls still running bash 2 or older really need an upgrade.
readonly y_re='^(Y|y)'
readonly mv_re='^([^/]*)/(.*)$'
readonly rh_kernels='(debug|summit|smp|enterprise|bigmem|hugemem|BOOT|vmnix)'

# Areas that will vary between Linux and other OS's
_get_kernel_dir() {
    if [[ -z $ksourcedir_fromcli ]]; then
        KVER=$1
        case ${current_os} in
           Linux)          DIR="/lib/modules/$KVER/build" ;;
           GNU/kFreeBSD)   DIR="/usr/src/kfreebsd-headers-$KVER/sys" ;;
        esac
        echo $DIR
    else
        echo $kernel_source_dir
    fi
}

_check_kernel_dir() {
    DIR=$(_get_kernel_dir $1)
    case ${current_os} in
       Linux)          test -e $DIR/include ;;
       GNU/kFreeBSD)   test -e $DIR/kern && test -e $DIR/conf/kmod.mk ;;
       *)              return 1 ;;
    esac
    return $?
}

# Run a command that we may or may not want to be detailed about.
invoke_command()
{
    # $1 = command to be executed using eval.
    # $2 = Description of command to run
    # $3 = 'background' if you want to run the command asynchronously.
    local exitval=0
    [[ $verbose ]] && echo -e "$1" || echo -en "$2..."
    if [[ $3 = background && ! $verbose ]]; then
        local pid progresspid
        (eval "$1" >/dev/null 2>&1) & pid=$!
        {
            on_exit() {
                kill $(jobs -p) 2>/dev/null
                wait $(jobs -p) 2>/dev/null
            }
            trap on_exit EXIT
            while [ -d /proc/$pid ]; do
                sleep 3 &
                wait $!
                echo -en "."
            done
        } & progresspid=$!
        wait $pid 2>/dev/null
        exitval=$?
        kill $progresspid 2>/dev/null
        wait $progresspid 2>/dev/null
    else
        eval "$1"; exitval=$?
    fi
    (($exitval > 0)) && echo -en "(bad exit status: $exitval)"
    echo -en "\n"
    return $exitval
}

error() (
    exec >&2
    echo -n $"Error! "
    for s in "$@"; do echo "$s"; done 
)

warn() (
    exec >&2
    echo -n $"Warning: "
    for s in "$@"; do echo "$s"; done
)

# Print an error message and die with the passed error code.
die() {
    # $1 = error code to return with
    # rest = strings to print before we exit.
    ret=$1
    shift
    error "$@"
    [[ $die_is_fatal = yes ]] && exit $ret || return $ret
}

mktemp_or_die() {
    local t
    t=$(mktemp "$@") && echo "$t" && return
    [[ $* = *-d* ]] && die 1 $"Unable to make temporary directory"
    die 1 "Unable to make temporary file."
}

show_usage()
{
    echo $"Usage: $0 [action] [options]"
    echo $"  [action]  = { add | remove | build | install | uninstall | match | autoinstall | mkdriverdisk |"
    echo $"                mktarball | ldtarball | mkrpm | mkkmp | mkdeb | mkdsc | mkbmdeb | status }"
    echo $"  [options] = [-m module] [-v module-version] [-k kernel-version] [-a arch]"
    echo $"              [-d distro] [-c dkms.conf-location] [-q] [--force] [--force-version-override] [--all]"
    echo $"              [--templatekernel=kernel] [--directive='cli-directive=cli-value']"
    echo $"              [--config=kernel-.config-location] [--archive=tarball-location]"
    echo $"              [--kernelsourcedir=source-location] [--no-prepare-kernel] [--no-initrd]"
    echo $"              [--binaries-only] [--source-only] [-r release (SuSE)] [--verbose]"
    echo $"              [--size] [--spec=specfile] [--media=floppy|iso|tar] [--legacy-postinst=0|1]"
    echo $"              [--no-depmod] [-j number] [--version]"
}

VER()
{
    # $1 = kernel version string

    # Pad all numbers in $1 so that they have at least three digits, e.g.,
    #   2.6.9-1cvs200409091247 => 002.006.009-001cvs200409091247
    # The result should compare correctly as a string.

    echo $1 | sed -e 's:\([^0-9]\)\([0-9]\):\1 \2:g' \
        -e 's:\([0-9]\)\([^0-9]\):\1 \2:g' \
        -e 's:\(.*\): \1 :' \
        -e 's: \([0-9]\) : 00\1 :g' \
        -e 's: \([0-9][0-9]\) : 0\1 :g' \
        -e 's: ::g'
}

# Find out how many CPUs there are so that we may pass an appropriate -j
# option to make. Ignore hyperthreading for now.
get_num_cpus()
{
   # use nproc(1) from coreutils 8.1-1+ if available, otherwise single job
   if [ -x /usr/bin/nproc ]; then
	nproc
   else
	echo "1"
  fi
}

# Finds a .ko or .ko.xz based on a directory and module name
# must call set_module_suffix first
compressed_or_uncompressed()
{
    # module dir = $1
    # module = $2
    local test1="$1/$2$module_uncompressed_suffix"
    local test2="$1/$2$module_uncompressed_suffix$module_compressed_suffix"
    if [[ -e "$test1" ]]; then
        echo "$test1"
    elif [[ -e "$test2" ]]; then
        echo "$test2"
    fi
}

# Finds .ko or .ko.xz based on a tree and module name
# must call set_module_suffix first
find_module()
{
    # tree = $1
    # module = $2
    find "$1" -name "$2$module_uncompressed_suffix" -o -name "$2$module_suffix" -type f
    return $?
}


# Figure out the correct module suffix for the kernel we are currently
# dealing with, which may or may not be the currently installed kernel.
set_module_suffix()
{
    # $1 = the kernel to base the module_suffix on
    kernel_test="${1:-$(uname -r)}"
    module_uncompressed_suffix=".ko"
    [[ $(VER $kernel_test) < $(VER 2.5) ]] && module_uncompressed_suffix=".o"
    grep -q '\.gz:' /lib/modules/$kernel_test/modules.dep 2>/dev/null && module_compressed_suffix=".gz"
    grep -q '\.xz:' /lib/modules/$kernel_test/modules.dep 2>/dev/null && module_compressed_suffix=".xz"
    module_suffix="$module_uncompressed_suffix$module_compressed_suffix"
}

set_kernel_source_dir()
{
    if [[ -z $ksourcedir_fromcli ]]; then
        # $1 = the kernel to base the directory on
        kernel_source_dir="$(_get_kernel_dir "$1")"
    fi
}

# A little test function for DKMS commands that only work on one kernel.
have_one_kernel() {
    if (( ${#kernelver[@]} != 1 )); then
        die 4 $"The action $1 does not support multiple kernel version" \
        $"parameters on the command line."
    fi
    if [[ $all ]]; then
        die 5 $"The action $1 does not support the --all" \
        $"parameter."
    fi
}

# Set up the kernelver and arch arrays.  You must have a 1:1 correspondence --
# if there is an entry in kernelver[$i], there must also be an entry in arch[$i]
# Note the special casing for the status action -- the status functions just
# report on what we already have, and will break with the preprocessing that
# this function provides.
setup_kernels_arches()
{
    # If all is set, use dkms status to fill the arrays
    if [[ $all && $1 != status ]]; then
        local i=0
        while read line; do
            line=${line#*/}; line=${line#*/};
            # (I would leave out the delimiters in the status output
            #  in the first place.)
            kernelver[$i]=${line%/*}
            arch[$i]=${line#*/}
            i=$(($i + 1))
        done < <(module_status_built "$module" "$module_version")
    fi

    # Set default kernel version and arch, if none set (but only --all isn't set)
    if [[ $1 != status ]]; then
        if [[ ! $kernelver && ! $all ]]; then
            kernelver[0]=$(uname -r)
            kernels_arches_default="yes"
        fi
        if [[ ! $arch ]]; then
            kernelver_rpm=$(rpm -qf "/lib/modules/$kernelver" 2>/dev/null | \
            grep -v "not owned by any package" | grep kernel | head -n 1)
            if ! arch[0]=$(rpm -q --queryformat "%{ARCH}" "$kernelver_rpm" 2>/dev/null); then
                arch[0]=$(uname -m)
                if [[ $arch = x86_64 ]] && grep -q Intel /proc/cpuinfo && ls $install_tree/$kernelver/build/configs 2>/dev/null | grep -q "ia32e"; then
                    arch[0]="ia32e"
                fi
            fi
        fi
    fi

    # If only one arch is specified, make it so for all the kernels
    if ((${#arch[@]} == 1 && ${#kernelver[@]} > 1)); then
        while ((${#arch[@]} < ${#kernelver[@]})); do
            arch[${#arch[@]}]=$arch
        done
    fi

    # Set global multi_arch
    multi_arch=""
    local i=0
    for ((i=0; $i < ${#arch[@]}; i++)); do
        [[ $arch != ${arch[$i]} ]] && {
            multi_arch="true"
            break
        }
    done
}

do_depmod()
{
    if [[ $no_depmod ]]; then
        return
    fi
    # $1 = kernel version
    if [ "${current_os}" != "Linux" ] ; then
        return
    fi
    if [[ -f /boot/System.map-$1 ]]; then
        depmod -a "$1" -F "/boot/System.map-$1"
    else
        depmod -a "$1"
    fi
}

# This function is a little hairy -- every distro has slightly different tools
# and naming conventions for creating initial ramdisks.  It should probably
# be split out into one function per distro, with make_initrd left as a stub.
make_initrd()
{
    # $1 = kernel version
    # $2 = arch
    # $3 = 'backup', if backup of old initrd is wanted (using .old-dkms filename suffix)
    
    [[ $no_initrd ]] && return
    local mkinitrd kernel_file initrd_dir="/boot"
    for mkinitrd in dracut update-initramfs mkinitrd ''; do
        [[ $mkinitrd ]] && which "$mkinitrd" >/dev/null 2>&1 && break
    done

    # No mkinitrd? Just return.
    [[ $mkinitrd ]] || return 0

    # Back up our current initrd
    echo $""
    # Find out what the proper filename will be
    for initrd in "initrd-$1.img" "initramfs-$1.img" "initrd.img-$1" "initrd-$1" ''; do
        [[ $initrd && -f $initrd_dir/$initrd ]] && break
    done
    if ! [[ $initrd ]]; then
        # Return if we cannot find an initrd.
        warn $"Unable to find an initial ram disk that I know how to handle." \
            $"Will not try to make an initrd."
        return 0
    fi
    if [[ $3 = backup ]]; then 
        echo $"Backing up $initrd to $initrd_dir/$initrd.old-dkms"
        cp -f "$initrd_dir/$initrd" "$initrd_dir/$initrd.old-dkms"
        echo $"Making new $initrd"
        echo $"(If next boot fails, revert to $initrd.old-dkms image)"
    fi

    if [[ $mkinitrd = dracut ]]; then
        invoke_command "$mkinitrd -f $initrd_dir/$initrd $1" "$mkinitrd" background
    elif [[ $mkinitrd = update-initramfs ]]; then
        invoke_command "$mkinitrd -u -k $1" "$mkinitrd" background
    elif $mkinitrd --version >/dev/null 2>&1; then
        invoke_command "$mkinitrd -f $initrd_dir/$initrd $1" "$mkinitrd" background
    elif [[ -e /etc/SuSE-release || -d /etc/SuSEconfig ]]; then
        for kernel_file in vmlinuz vmlinux ''; do
            [[ $kernel_file && -f $initrd_dir/$kernel_file ]] && break
        done
        if [[ ! $kernel_file ]]; then
            error $"Unable to find valid kernel file under " \
            $"$initrd_dir for kernel version $1"
            return 1;
        fi
        invoke_command "$mkinitrd -k $kernel_file-$1 -i $initrd" "$mkinitrd" background
    elif [[ -e /etc/debian_version ]]; then
        invoke_command "$mkinitrd -o $initrd_dir/$initrd $1" "$mkinitrd" background
    else
        echo $""
        echo $"Calling $mkinitrd (bad exit status 9 may occur)"
        invoke_command "$mkinitrd" "$mkinitrd" background
    fi
    return
}

# Grab our distro information from RPM-based distros.
distro_version_rpm()
{
    which rpm > /dev/null 2>&1 || { echo unknown; return; }
    local r wp ver dist

    for r in redhat-release sles-release suse-release ovs-release; do
        wp=$(rpm -q --whatprovides "$r") || continue
        ver=$(rpm -q --qf "%{version}\n" ${wp})
        case $r in
            sles*)
                echo sles${ver}
                ;;
            suse*)
                echo suse${ver}
                ;;
            ovs*)
                echo ovm${ver}
                ;;
            redhat*)
                case $wp in
                    redhat*|sl*)
                        ver=$(echo $ver | \
                        sed -e 's/^\([[:digit:]]*\).*/\1/g')
                        echo el${ver}
                        ;;
                    centos*|enterprise*)
                        echo el${ver}
                        ;;
                    fedora*)
                        echo fc${ver}
                        ;;
                    *)
                        echo unknown
                        ;;
                esac
                ;;
            *)
                echo unknown
                ;;
        esac
        return
    done
    echo unknown
}

# Grab distro information from LSB compliant distros.
# Falls back to distro_version_rpm if needed.
distro_version()
{
    # What distribution are we running?
    local LSB_DESCRIPTION DISTRIB_ID DISTRIB_RELEASE ver

    # Try the LSB-provided strings first
    if [ -r /etc/lsb-release ]; then
        . /etc/lsb-release
    elif type lsb_release >/dev/null 2>&1; then
        DISTRIB_ID=$(lsb_release -i -s)
        DISTRIB_RELEASE=$(lsb_release -r -s)
    fi

    case ${DISTRIB_ID} in
        Fedora)
            echo fc${DISTRIB_RELEASE}
            ;;
        RedHatEnterprise*|CentOS|ScientificSL)
            # OEL also reports as such; format is 4.7, 5.3
            ver=$(echo "${DISTRIB_RELEASE}" | \
            sed -e 's/^\([[:digit:]]*\).*/\1/g')
            echo el${ver}
            ;;
        SUSE*)
            if [[ $(lsb_release -d -s) =~ Enterprise ]]; then
                echo sles${DISTRIB_RELEASE}
            else
                echo suse${DISTRIB_RELEASE}
            fi
            ;;
        *)
            if [[ ${DISTRIB_ID} && ${DISTRIB_RELEASE} ]]; then
                echo "${DISTRIB_ID}${DISTRIB_RELEASE}"
            else
                distro_version_rpm
            fi
            ;;
    esac
}

override_dest_module_location()
{
    local orig_location="$1"
    [[ ${addon_modules_dir} ]] && echo "/${addon_modules_dir}" && return

    if [ "$current_os" = "GNU/kFreeBSD" ] ; then
        # Does not support subdirs, regardless of distribution
        echo "" && return
    fi

    case "$running_distribution" in
    sles[123456789])
        ;;
    suse[123456789])
        ;;
    suse10\.[01])
        ;;
    fc*)
        echo "/extra" && return
        ;;
    el*)
        echo "/extra" && return
        ;;
    ovm*)
        echo "/extra" && return
        ;;
    sles*)
        echo "/updates" && return
        ;;
    suse*)
        echo "/updates" && return
        ;;
    Ubuntu*)
        echo "/updates/dkms" && return
        ;;
    Debian*)
        echo "/updates/dkms" && return
        ;;
    *)
        ;;
    esac
    echo "$orig_location"
}

# Source a file safely.
# We want to ensure that the .conf file we source does not stomp all over
# parts of the environment we don't want them to.  This makes it so that
# it is harder to accidentally corrupt our environment.  conf files can
# still deliberatly trash the environment by abusing dkms_directive env
# variables or by crafting special values that will make eval do evil things.
safe_source() {
    # $1 = file to source
    # $@ = environment variables to echo out
    local to_source_file="$1"; shift
    declare -a -r export_envs=("$@")
    local tmpfile=$(mktemp_or_die)
    ( exec >"$tmpfile"
    . "$to_source_file" >/dev/null
    # This is really ugly, but a neat hack
    # Remember, in bash 2.0 and greater all variables are really arrays.
    for _export_env in "${export_envs[@]}"; do
        for _i in $(eval echo \${!$_export_env[@]}); do
            eval echo '$_export_env[$_i]=\"${'$_export_env'[$_i]}\"'
        done
    done

    # handle DKMS_DIRECTIVE stuff specially.
    for directive in $(set | grep ^DKMS_DIRECTIVE | cut -d = -f 2-3); do
        directive_name=${directive%%=*}
        directive_value=${directive#*=}
        echo "$directive_name=\"$directive_value\""
    done
    )
    . "$tmpfile"
    rm "$tmpfile"
}

# Source a dkms.conf file and perform appropriate postprocessing on it.
# Do our best to not repeatedly source the same .conf file -- this can happen
# when chaining module installtion functions or autoinstalling.
read_conf()
{
    # $1 kernel version (required)
    # $2 arch (required)
    # $3 dkms.conf location (optional)

    local return_value=0
    local read_conf_file="$dkms_tree/$module/$module_version/source/dkms.conf"

    # Set variables supported in dkms.conf files (eg. $kernelver)
    local kernelver="$1"
    local arch="$2"
    set_kernel_source_dir "$1"


    # Find which conf file to check
    [[ $conf ]] && read_conf_file="$conf"
    [[ $3 ]] && read_conf_file="$3"

    [[ -r $read_conf_file ]] || die 4 $"Could not locate dkms.conf file." \
    $"File: $read_conf_file does not exist."

    [[ $last_mvka = $module/$module_version/$1/$2 && \
    $last_mvka_conf = $(readlink -f $read_conf_file) ]] && return


    # Clear variables and arrays
    for var in $dkms_conf_variables; do
        unset $var
    done

    # Source in the dkms.conf.
    # Allow for user-specified overrides in order of specificity.
    local _conf_file
    for _conf_file in "$read_conf_file" "/etc/dkms/$module.conf" \
        "/etc/dkms/$module-$module_version.conf" "/etc/dkms/$module-$module_version-$1.conf" \
        "/etc/dkms/$module-$module_version-$1-$2.conf"; do
        [ -e "$_conf_file" ] && safe_source "$_conf_file" $dkms_conf_variables
    done

    # Source in the directive_array
    for directive in "${directive_array[@]}"; do
        directive_name=${directive%%=*}
        directive_value=${directive#*=}
        export $directive_name="$directive_value"
        echo $"DIRECTIVE: $directive_name=\"$directive_value\""
    done

    # Set variables
    clean="$CLEAN"
    package_name="$PACKAGE_NAME"
    package_version="$PACKAGE_VERSION"
    post_add="$POST_ADD"
    post_build="$POST_BUILD"
    post_install="$POST_INSTALL"
    post_remove="$POST_REMOVE"
    pre_build="$PRE_BUILD"
    pre_install="$PRE_INSTALL"
    obsolete_by="$OBSOLETE_BY"
    sign_tool="$SIGN_TOOL"

    # Set module naming/location arrays
    local index array_size=0 s
    for s in ${#BUILT_MODULE_NAME[@]} \
        ${#BUILT_MODULE_LOCATION[@]} \
        ${#DEST_MODULE_NAME[@]} \
        ${#DEST_MODULE_LOCATION[@]}; do
        ((s > array_size)) && array_size=$s
    done
    for ((index=0; index < array_size; index++)); do
        # Set values
        built_module_name[$index]=${BUILT_MODULE_NAME[$index]}
        built_module_location[$index]=${BUILT_MODULE_LOCATION[$index]}
        dest_module_name[$index]=${DEST_MODULE_NAME[$index]}
        dest_module_location[$index]=${DEST_MODULE_LOCATION[$index]}
        modules_conf_obsoletes[$index]=${MODULES_CONF_OBSOLETES[$index]}
        modules_conf_alias_type[$index]=${MODULES_CONF_ALIAS_TYPE[$index]}
        case ${MODULES_CONF_OBSOLETE_ONLY[$index]} in
            [yY]*)
                modules_conf_obsolete_only[$index]="yes"
                ;;
        esac
        case ${STRIP[$index]} in
            [nN]*)
                strip[$index]="no"
                ;;
            [yY]*)
                strip[$index]="yes"
                ;;
            '')
                strip[$index]=${strip[0]:-yes}
                ;;
        esac

        # If unset, set by defaults
        [[ ! ${built_module_name[$index]} ]] && \
            ((${#DEST_MODULE_LOCATION[@]} == 1)) && \
            built_module_name[$index]=$module
        [[ ! ${dest_module_name[$index]} ]] && \
            dest_module_name[$index]=${built_module_name[$index]}
        [[ ${built_module_location[$index]} && \
            ${built_module_location[$index]:(-1)} != / ]] && \
            built_module_location[$index]="${built_module_location[$index]}/"

        # FAIL if no built_module_name
        if [[ ! ${built_module_name[$index]} ]]; then
            echo $"dkms.conf: Error! No 'BUILT_MODULE_NAME' directive specified for record #$index." >&2
            return_value=1
        fi

        # FAIL if built_module_name ends in .o or .ko
        case ${built_module_name[$index]} in
            *.o|*.ko)
                echo $"dkms.conf: Error! 'BUILT_MODULE_NAME' directive ends in '.o' or '.ko' in record #$index." >&2
                return_value=1
                ;;
        esac

        # FAIL if dest_module_name ends in .o or .ko
        case ${dest_module_name[$index]} in
            *.o|*.ko)
                echo $"dkms.conf: Error! 'DEST_MODULE_NAME' directive ends in '.o' or '.ko' in record #$index." >&2
                return_value=1
                ;;
        esac

        # Override location for specific kernels
        dest_module_location[$index]="$(override_dest_module_location ${dest_module_location[$index]})"

        # Fail if no DEST_MODULE_LOCATION
        if [[ ! ${DEST_MODULE_LOCATION[$index]} ]]; then
            echo $"dkms.conf: Error! No 'DEST_MODULE_LOCATION' directive specified for record #$index.">&2
            return_value=1
        fi
            # Fail if bad DEST_MODULE_LOCATION
        case ${DEST_MODULE_LOCATION[$index]} in
            /kernel*)
                ;;
            /updates*)
                ;;
            /extra*)
                ;;
            *)
            echo $"dkms.conf: Error! Directive 'DEST_MODULE_LOCATION' does not begin with">&2
            echo $"'/kernel', '/updates', or '/extra' in record #$index.">&2
            return_value=1
            ;;
        esac
    done

    # Get the correct make command
    [[ ${MAKE_MATCH[0]} ]] || make_command="${MAKE[0]}"
    for ((index=0; index < ${#MAKE[@]}; index++)); do
    [[ ${MAKE[$index]} && ${MAKE_MATCH[$index]} && \
        $1 =~ ${MAKE_MATCH[$index]} ]] && \
        make_command="${MAKE[$index]}"
    done

    # Use the generic make and make clean commands if not specified
    if [[ $(VER $1) < $(VER 2.6.6) ]]; then
        [[ ! $make_command ]] && make_command="make -C $kernel_source_dir SUBDIRS=$dkms_tree/$module/$module_version/build modules"
        [[ ! $clean ]] && clean="make -C $kernel_source_dir SUBDIRS=$dkms_tree/$module/$module_version/build clean"
    else
        [[ ! $make_command ]] && make_command="make -C $kernel_source_dir M=$dkms_tree/$module/$module_version/build"
        [[ ! $clean ]] && clean="make -C $kernel_source_dir M=$dkms_tree/$module/$module_version/build clean"
    fi

    # Set modules_conf_array
    for ((index=0; index < ${#MODULES_CONF[@]}; index++)); do
        [[ ${MODULES_CONF[$index]} ]] && modules_conf_array[$index]="${MODULES_CONF[$index]}"
    done

    # Set patch_array (including kernel specific patches)
    count=0
    for ((index=0; index < ${#PATCH[@]}; index++)); do
    if [[ ${PATCH[$index]} && (! ${PATCH_MATCH[$index]} || $1 =~ ${PATCH_MATCH[$index]}) ]]; then
        patch_array[$count]="${PATCH[$index]}"
        count=$(($count+1))
    fi
    done

    # Set remake_initrd
    [[ $REMAKE_INITRD =~ $y_re ]] && remake_initrd="yes"

    # Set build_exclude
    [[ $BUILD_EXCLUSIVE_KERNEL && ! $1 =~ $BUILD_EXCLUSIVE_KERNEL ]] && build_exclude="yes"
    [[ $BUILD_EXCLUSIVE_ARCH && ! $2 =~ $BUILD_EXCLUSIVE_ARCH ]] && build_exclude="yes"

    # Fail if absolutely no DEST_MODULE_LOCATION
    if ((${#dest_module_location[@]} == 0)); then
        echo $"dkms.conf: Error! No 'DEST_MODULE_LOCATION' directive specified." >&2
        return_value=1
    fi

    # Fail if no PACKAGE_NAME
    if [[ ! $package_name ]]; then
        echo $"dkms.conf: Error! No 'PACKAGE_NAME' directive specified.">&2
        return_value=1
    fi

    # Fail if no PACKAGE_VERSION
    if [[ ! $package_version ]]; then
        echo $"dkms.conf: Error! No 'PACKAGE_VERSION' directive specified.">&2
        return_value=1
    fi

    # Set clean
    [[ $clean ]] || clean="make clean"

    ((return_value == 0)) && last_mvka="$module/$module_version/$1/$2" && last_mvka_conf="$(readlink -f "$read_conf_file")"
    return $return_value
}

# Little helper function for parsing the output of modinfo.
get_module_verinfo(){
    res=("" "" "")
    local vals=
    while read -a vals; do
    case ${vals[0]} in
        version:)
            res[0]=${vals[1]}
            res[2]=${vals[2]}
            ;;
        srcversion:)
        res[1]=${vals[1]}
        ;;
    esac
    done < <(modinfo $1)
}

# Perform some module version sanity checking whenever we are installing
# or removing modules.
check_version_sanity()
{
    # $1 = kernel_version
    # $2 = arch
    # $3 = obs by kernel version
    # $4 = dest_module_name

    local lib_tree="$install_tree/$1" res=
    echo $"Running module version sanity check."
    local i=0
    local -a kernels_info dkms_info
    if [ -n $3 ]; then
        # Magic split into array syntax saves trivial awk and cut calls.
        local -a obs=(${3//-/ })
        local -a my=(${1//-/ })
        local obsolete=0
        if [[ ${obs} && ${my} ]]; then
            if [[ $(VER ${obs}) == $(VER ${my}) && ! $force ]]; then
                # They get obsoleted possibly in this kernel release
                if [[ ! ${obs[1]} ]]; then
                    # They were obsoleted in this upstream kernel
                    obsolete=1
                elif [[ $(VER ${my[1]}) > $(VER ${obs[1]}) ]]; then
                    # They were obsoleted in an earlier ABI bump of the kernel
                    obsolete=1
                elif [[ $(VER ${my[1]}) = $(VER ${obs[1]}) ]]; then
                    # They were obsoleted in this ABI bump of the kernel
                    obsolete=1
                fi
            elif [[ $(VER ${my}) > $(VER ${obs}) && ! $force ]]; then
                # They were obsoleted in an earlier kernel release
                obsolete=1
            fi
        fi

        if ((obsolete == 1)); then
            echo $"" >&2
            echo $"Module has been obsoleted due to being included" >&2
            echo $"in kernel $3.  We will avoid installing" >&2
            echo $"for future kernels above $3." >&2
            echo $"You may override by specifying --force." >&2
            return 1
        fi
    fi
    set_module_suffix
    read -a kernels_module < <(find_module "$lib_tree" "${4}")
    [ -z $kernels_module ] && return 0

    if [[ "$force_version_override" == "true" ]]; then
        # Skip the following version checking code.
        return 0
    fi

    if [[ ${kernels_module[1]} ]]; then
        warn $"Warning! Cannot do version sanity checking because multiple ${4}$module_suffix" \
            $"modules were found in kernel $1."
        return 0
    fi
    local dkms_module=$(compressed_or_uncompressed "$dkms_tree/$module/$module_version/$1/$2/module/" "${4}")
    get_module_verinfo $kernels_module; kernels_info=("${res[@]}")
    get_module_verinfo $dkms_module; dkms_info=("${res[@]}")
    if [[ ! ${dkms_info[1]} && ${kernels_info[1]} ]]; then
        # Use obsolete checksum info
        dkms_info[1]=${dkms_info[2]}
        kernels_info[1]=${kernels_info[2]}
    fi

    if [[ ${kernels_info[1]} && ${dkms_info[1]} && ${kernels_info[1]} = ${dkms_info[1]} &&
	  -n "${kernels_info[0]}" && -n "${dkms_info[0]}" && ${kernels_info[0]} = ${dkms_info[0]} && ! $force ]]; then
        echo $"" >&2
        echo $"Good news! Module version $dkms_info for ${4}$module_suffix" >&2
        echo $"exactly matches what is already found in kernel $1." >&2
        echo $"DKMS will not replace this module." >&2
        echo $"You may override by specifying --force." >&2
        return 1
    fi

    if [[ $kernels_info && $dkms_info && ! ( $(VER $dkms_info) > $(VER $kernels_info) ) && ! $force ]]; then
        error $"Module version $dkms_info for ${4}$module_suffix" \
            $"is not newer than what is already found in kernel $1 ($kernels_info)." \
            $"You may override by specifying --force."
        return 1
    fi

    return 0
}

moduleconfig_update_obsoletes()
{
    # $@ = files to process
    # Do nothing if we have no obsoletes
    ( IFS=; [[ "${modules_conf_obsoletes[*]}" ]] ) || return 0
    # Generate sed args to remove obsolete modules
    local mod_diff
    for ((index=0; index < ${#dest_module_name[@]}; index++)); do
        [[ ${modules_conf_obsoletes[$index]} ]] || continue
        for obsolete_module in ${modules_conf_obsoletes[$index]//,/ }; do
            # For module.conf style syntax
            sa_mc_o[${#sa_mc_o[*]}]="-e"
            sa_mc_o[${#sa_mc_o[*]}]="s/\(alias ${modules_conf_alias_type[$index]}[0-9]*\) $obsolete_module$/\1 ${dest_module_name[$index]}/g"
            # For /etc/sysconfig/kernel style syntax
            sa_sck_o[${#sa_sck_o[*]}]="-e"
            sa_sck_o[${#sa_sck_o[*]}]="s/\(INITRD_MODULES.*\)$obsolete_module\b\(.*\)/\1${dest_module_name[$index]}\2/"
        done
    done

    # Do all the changes at once, record the diffs for posterity
    for file in "$@"; do
        [[ $file && -w $file ]] || continue
        _tmpf="$temp_dir_name/${file##*/}.new"
        if [[ $file = /etc/sysconfig/kernel ]]; then
            [ -z "${sa_sck_o[@]}" ] || sed "${sa_sck_o[@]}" "$file" > "$_tmpf"
        else
            [ -z "${sa_mc_o[@]}" ] || sed "${sa_mc_o[@]}" "$file" > "$_tmpf"
        fi
	if [ -f "$_tmpf" ] && ! mod_diff=$(diff -u "$_tmpf" "$file"); then
            echo $"$file updated to replace obsoleted module references:"
            echo "$mod_diff"
            cp -fp "$_tmpf" "$file"
            rm -f "$_tmpf"
        fi
    done
}

moduleconfig_add()
{
    # $1 = kernel version

    local temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    local -a sa_mc_o=() sa_sck_o=()
    modconfig_files="/etc/modprobe.d/dkms.conf
        /etc/modprobe.d/dkms
        /etc/modules.conf
        /etc/modprobe.conf
        /etc/modprobe.d/$package_name.conf
        /etc/sysconfig/kernel"

    moduleconfig_update_obsoletes $modconfig_files

    for moduleconfig in $modconfig_files; do
    [[ -e $moduleconfig ]] || continue
    for ((index=0; index < ${#dest_module_name[@]}; index++)); do

        # Only add it if it can't be found already in config file
        if [[ ${modules_conf_alias_type[$index]} ]] && \
           ! grep -qs "alias ${modules_conf_alias_type[$index]}[0-9]* ${dest_module_name[$index]}\b" $moduleconfig && \
           [[ ${modules_conf_obsolete_only[$index]} != yes ]]; then
            if [[ $modconfig_files = /etc/modprobe.d/$package_name.conf ]] && [[ ! -e /etc/modprobe.d/$package_name.conf ]]; then
                touch /etc/modprobe.d/$package_name.conf
                echo $"created /etc/modprobe.d/$package_name.conf.">&2
            fi
            aliases=$(awk "/^alias ${modules_conf_alias_type[$index]}/ {print \$2}" $moduleconfig)
            if [[ $aliases ]]; then
                alias_number=$(($(echo "$aliases" | sed "s/${modules_conf_alias_type[$index]}//" | sort -n | tail -n 1) + 1))
            else
                alias_number=0
            fi
            echo -e "alias ${modules_conf_alias_type[$index]}${alias_number} ${dest_module_name[$index]}" >> $moduleconfig
            echo $"$moduleconfig: added alias reference for '${dest_module_name[$index]}'"
        fi
    done

    # Add anything else
    for ((index=0; index < ${#modules_conf_array[@]}; index++)); do
        if [ -n "${modules_conf_array[$index]}" ] && \
        ! grep -q "${modules_conf_array[$index]}" "$moduleconfig"; then
        echo -e $"$moduleconfig: added '${modules_conf_array[$index]}'"
        echo -e "${modules_conf_array[$index]}" >> $moduleconfig
        fi
    done
    done

    # Delete the temp dir
    rm -rf $temp_dir_name
}

moduleconfig_remove()
{
    # $1 = kernel version

    local temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    modconfig_files=""
    [ -e /etc/modprobe.d/dkms.conf ] && modconfig_files="/etc/modprobe.d/dkms.conf"
    [ -e /etc/modprobe.d/dkms ] && modconfig_files="/etc/modprobe.d/dkms"
    [ -e /etc/modules.conf ] && modconfig_files="$modconfig_files /etc/modules.conf"
    [ -e /etc/modprobe.conf ] && modconfig_files="$modconfig_files /etc/modprobe.conf"
    [ -e /etc/modprobe.d/$package_name.conf ] && modconfig_files="/etc/modprobe.d/$package_name.conf"

    for moduleconfig in $modconfig_files; do
        for ((index=0; index < ${#dest_module_name[@]}; index++)); do
            # Remove/Replace aliases (maybe)
            [[ ${modules_conf_alias_type[$index]} ]] || continue
            find "$install_tree/$1/" -name "${dest_module_name[$index]}.*" -quit 2>/dev/null && continue

            local conf_replacement=""
            for obsolete_module in ${modules_conf_obsoletes[$index]//,/ }; do
                find $install_tree/$1/ -name "$obsolete_module.*" -quit 2>/dev/null || continue
                conf_replacement=$obsolete_module
                break
            done

            if [[ ! $conf_replacement ]]; then
                grep -v "alias ${modules_conf_alias_type[$index]}[0-9]* ${dest_module_name[$index]}" $moduleconfig > $temp_dir_name/moduleconfig.new
                mv -f $temp_dir_name/moduleconfig.new $moduleconfig
                echo $"$moduleconfig: removed alias for '${dest_module_name[$index]}'"
                if [[ $modconfig_files = /etc/modprobe.d/$package_name.conf ]]; then
                    rm -f /etc/modprobe.d/$package_name.conf
                    echo $"$moduleconfig: deleted /etc/modprobe.d/$package_name.conf file"
                fi
                elif grep -q "alias ${modules_conf_alias_type[$index]}[0-9]* ${dest_module_name[$index]}$" $moduleconfig; then
                    sed "s/\(alias ${modules_conf_alias_type[$index]}[0-9]*\) ${dest_module_name[$index]}$/\1 $conf_replacement/g" \
                        $moduleconfig > $temp_dir_name/moduleconfig.new
                    mv -f $temp_dir_name/moduleconfig.new $moduleconfig
                    echo $"$moduleconfig: alias for '${dest_module_name[$index]}' changed back to '$conf_replacement'"
            fi
        done

        # Remove static conf entries
        for ((index=0; index < ${#modules_conf_array[@]}; index++)); do
            [[ ${modules_conf_array[$index]} ]] || continue
            grep -v "${modules_conf_array[$index]}" "$moduleconfig" > $temp_dir_name/moduleconfig.new
            echo $"$moduleconfig: removed '${modules_conf_array[$index]}'"
            mv -f $temp_dir_name/moduleconfig.new $moduleconfig
        done
    done

    # Delete the temp dir
    rm -rf $temp_dir_name
}

etc_sysconfig_kernel_modify()
(
    [[ -e /etc/sysconfig/kernel && $remake_initrd ]] || return 0

    # Make a temp directory to store files
    local temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    if [[ $1 = add ]]; then
        . /etc/sysconfig/kernel
        for m in "${dest_module_name[@]}"; do
            for l in "${INITRD_MODULES}"; do
                [[ $m = $l ]] && continue 2
            done
            sed -e "s/INITRD_MODULES=\"\(.*\)\"/INITRD_MODULES=\"\1 $m\"/" /etc/sysconfig/kernel > $temp_dir_name/kernel.new
            mv $temp_dir_name/kernel.new /etc/sysconfig/kernel
        done
    # Remove /etc/sysconfig/kernel entries
    elif [[ $1 = delete ]]; then
        for m in "${dest_module_name[@]}"; do
            sed -e "s/\(INITRD_MODULES.*\)$m\b\(.*\)/\1\2/" /etc/sysconfig/kernel > $temp_dir_name/kernel.new
            mv $temp_dir_name/kernel.new /etc/sysconfig/kernel
        done
    fi
    # Delete the temp dir
    rm -rf $temp_dir_name
)

check_module_args() {
    [[ $module && $module_version ]] && return
    die 1 $"Invalid number of arguments passed." \
        $"Usage: $1 <module>/<module-version> or" \
        $"       $1 -m <module>/<module-version> or" \
        $"       $1 -m <module> -v <module-version>"
}

read_conf_or_die() {
    read_conf "$@" && return
    die 8 $"Bad conf file." $"File: $conf" \
        $"does not represent a valid dkms.conf file."
}

run_build_script() {
    # $1 = script type
    # $2 = script to run
    local script_type run
    [[ $2 ]] || return 0
    case "$1" in
        pre_build|post_build)
            script_type='build'
            ;;
        *)
            script_type='source'
            ;;
    esac
    run="$dkms_tree/$module/$module_version/$script_type/$2"
    if [[ -x ${run%% *} ]]; then
        echo $""
        echo $"Running the $1 script:"
        (
            cd "$dkms_tree/$module/$module_version/$script_type/"
            exec $run
        )
    else
        echo $""
        warn $"The $1 script is not executable."
    fi
}

run_sign_tool() {
    # $1 = sign tool to run
    # $2 = module file to sign
    local logfile="$base_dir/log/make.log"
    if [[ -x "$1" ]]; then
        echo "Signing $2" >> "$logfile"
        "$1" "$kernelver" "$2" || \
        report_build_problem 11 $"Bad exit status $? for sign tool." \
            $"Consult $logfile for more information."
    else
        echo $""
        warn $"The $1 is not executable."
    fi
}

# Register a DKMS-ified source tree with DKMS.
# This function is smart enough to register the module if we
# passed a source tree or a tarball instead of relying on the source tree
# being unpacked into /usr/src/$module-$module_version.
add_module()
{
    # If $archive is set and $module and $module_version are not,
    # try loading the tarball passed first.
    if [[ $archive_location && ! $module && ! $module_version ]]; then
        load_tarball
    elif [[ $try_source_tree && ! $module && ! $module_version ]]; then
        add_source_tree "$try_source_tree"
    fi

    # Check that we have all the arguments
    check_module_args add

    # Check that this module-version hasn't already been added
    if is_module_added "$module" "$module_version"; then
        die 3 $"DKMS tree already contains: $module-$module_version" \
            $"You cannot add the same module/version combo more than once."
    fi

    [[ $conf ]] || conf="$source_tree/$module-$module_version/dkms.conf"

    # Check that /usr/src/$module-$module_version exists
    if ! [[ -d $source_tree/$module-$module_version ]]; then
        die 2 $"Could not find module source directory." \
            $"Directory: $source_tree/$module-$module_version does not exist."
    fi

    # Do stuff for --rpm_safe_upgrade
    if [[ $rpm_safe_upgrade ]]; then
        local pppid=$(awk '/PPid:/ {print $2}' /proc/$PPID/status)
        local lock_name=$(mktemp_or_die $tmp_location/dkms_rpm_safe_upgrade_lock.$pppid.XXXXXX)
        echo "$module-$module_version" >> $lock_name
        ps -o lstart --no-headers -p $pppid 2>/dev/null >> $lock_name
    fi

    # Check the conf file for sanity
    read_conf_or_die "$kernelver" "$arch" "$conf"

    # Create the necessary dkms tree structure
    echo $""
    echo $"Creating symlink $dkms_tree/$module/$module_version/source ->"
    echo $"                 $source_tree/$module-$module_version"
    mkdir -p "$dkms_tree/$module/$module_version/build"
    ln -s "$source_tree/$module-$module_version" "$dkms_tree/$module/$module_version/source"

    # Run the post_add script
    run_build_script post_add "$post_add"

    echo $""
    echo $"DKMS: add completed."
}

# Prepare a kernel source or include tree for compiling a module.
# Most modern-ish distros do not require this function at all,
# so it will be removed in a future release.
prepare_kernel()
{
    # $1 = kernel version to prepare
    # $2 = arch to prepare

    set_kernel_source_dir "$1"

    # Check that kernel-source exists
    _check_kernel_dir "$1" || {
    case "$running_distribution" in
        Debian* | Ubuntu* )
            die 1 $"Your kernel headers for kernel $1 cannot be found." \
                $"Please install the linux-headers-$1 package," \
                $"or use the --kernelsourcedir option to tell DKMS where it's located"
                ;;
        * )
            die 1 echo $"Your kernel headers for kernel $1 cannot be found at" \
                $"/lib/modules/$1/build or /lib/modules/$1/source." \
                $"You can use the --kernelsourcedir option to tell DKMS where it's located."
            ;;
    esac
    }

    [[ $no_prepare_kernel ]] && return

    if [[ (! ( $(VER $1) < $(VER 2.6.5) ) || -d /etc/SuSEconfig) && \
        -d "$kernel_source_dir" && \
        -z "$ksourcedir_fromcli" ]]; then
        echo $""
        echo $"Kernel preparation unnecessary for this kernel.  Skipping..."
        no_clean_kernel="no-clean-kernel"
        return 1
    fi

    # Prepare kernel for module build
    echo $""
    echo $"Preparing kernel $1 for module build:"
    echo $"(This is not compiling a kernel, just preparing kernel symbols)"
    cd $kernel_source_dir
    [[ -r .config ]] && {
        config_contents=$(cat .config)
        echo $"Storing current .config to be restored when complete"
    }

    # Set kernel_config
    if [[ -e /etc/redhat-release || -e /etc/fedora-release ]]; then
    # Note this also applies to VMware 3.x
    if [[ -z $kernel_config && -d $kernel_source_dir/configs ]]; then
        local kernel_trunc=${1%%-*}
        # Try a .config specific to whatever kernel we are running
        if [[ $1 =~ $rh_kernels && -e $kernel_source_dir/configs/kernel-$kernel_trunc-$2-${BASH_REMATCH[1]}.config ]]; then
            kernel_config="$kernel_source_dir/configs/kernel-$kernel_trunc-$2-${BASH_REMATCH[1]}.config"
        elif [[ -e $kernel_source_dir/configs/kernel-$kernel_trunc-$2.config ]]; then
            # If that one does not exist, try a generic one.
            kernel_config="$kernel_source_dir/configs/kernel-$kernel_trunc-$2.config"
        else
            # If that does not exist, fall back to no config file
            kernel_config=""
        fi
    fi
    elif [[ (-e /etc/SuSE-release || -d /etc/SuSEconfig) && -z $kernel_config && -d $kernel_source_dir/arch ]]; then
        local kernel_trunc=${1%%-*}
        case $2 in
            i586|i686)
                config_arch="i386"
                ;;
            *)
                config_arch=$2
                ;;
        esac
        for config_type in default smp bigsmp; do
            [[ $1 =~ $config_type ]] && kernel_config="$kernel_source_dir/arch/$config_arch/defconfig.$config_type"
            [[ -e $kernel_config ]] || kernel_config=""
        done
        [[ $kernel_config ]] || kernel_config="$kernel_source_dir/arch/$config_arch/defconfig.default"
        [[ -e $kernel_config ]] || kernel_config=""
    fi

    # Do preparation
    if [ -e /boot/vmlinuz.version.h ]; then
        echo $"Running UnitedLinux preparation routine"
        local kernel_config="/boot/vmlinuz.config"
        invoke_command "make mrproper" "make mrproper" background
        [[ $config_contents ]] && echo "$config_contents" > .config
        invoke_command "cp /boot/vmlinuz.version.h include/linux/version.h" "using /boot/vmlinux.version.h"
        invoke_command "cp -f $kernel_config .config" "using $kernel_config"
        invoke_command "make KERNELRELEASE=$1 cloneconfig" "make cloneconfig" background
        invoke_command "make -j$parallel_jobs CONFIG_MODVERSIONS=1 KERNELRELEASE=$1 dep" "make CONFIG_MODVERSIONS=1 dep" background
    elif grep -q rhconfig.h $kernel_source_dir/include/linux/{modversions,version}.h 2>/dev/null; then
        echo $"Running Red Hat style preparation routine"
        invoke_command "make clean" "make clean" background
        [[ $config_contents ]] && echo "$config_contents" > .config

        if [[ $kernel_config ]]; then
            echo $"using $kernel_config"
            cp -f "$kernel_config" .config
        elif [[ -e .config ]]; then
            warn $"Using $kernel_source_dir/.config" \
                $"(I hope this is the correct config for this kernel)"
        else
            warn $"Cannot find a .config file to prepare your kernel with." \
                $"Try using the --config option to specify where one can be found." \
                $"Your build will likely fail because of this."
        fi

        # Hack to workaround broken tmp_include_depends for Red Hat
        if grep -q "/usr/src/build" $kernel_source_dir/tmp_include_depends 2>/dev/null; then
            sed 's/\/usr\/src\/build\/.*\/install//g' $kernel_source_dir/tmp_include_depends > $kernel_source_dir/tmp_include_depends.new
            mv -f $kernel_source_dir/tmp_include_depends.new $kernel_source_dir/tmp_include_depends
        fi

        invoke_command "make KERNELRELEASE=$1 oldconfig" "make oldconfig" background
        kerneldoth_contents=$(cat /boot/kernel.h 2>/dev/null)
        invoke_command "/usr/lib/dkms/mkkerneldoth --kernelver $1 --targetarch $2 --output /boot/kernel.h" "running mkkerneldoth" background
    else
        echo $"Running Generic preparation routine"
        invoke_command "make mrproper" "make mrproper" background
        [[ $config_contents ]] && echo "$config_contents" > .config

        if [[ $kernel_config ]]; then
            echo $"using $kernel_config"
            cp -f "$kernel_config" .config
        elif [[ -e .config ]]; then
            warn $"using $kernel_source_dir/.config" \
                $"(I hope this is the correct config for this kernel)"
        else
            warn $"Warning! Cannot find a .config file to prepare your kernel with." \
                $"Try using the --config option to specify where one can be found." \
                $"Your build will likely fail because of this."
        fi

        invoke_command "make KERNELRELEASE=$1 oldconfig" "make oldconfig" background
        if [[ $(VER $1) < $(VER 2.5) ]]; then
            invoke_command "make -j$parallel_jobs KERNELRELEASE=$1 dep" "make dep" background
        else
            invoke_command "make -j$parallel_jobs KERNELRELEASE=$1 prepare-all scripts" "make prepare-all" background
        fi
    fi
    cd - >/dev/null
}

# Get ready to build a module that has been registered with DKMS.
prepare_build()
{
    # If the module has not been added, try to add it.
    is_module_added "$module" "$module_version" || add_module

    set_kernel_source_dir "$kernelver"
    local base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch"

    # Check that the right arguments were passed
    check_module_args build

    # Check that the module has not already been built for this kernel
    [[ -d $base_dir ]] && die 3 \
        $"This module/version has already been built on: $kernelver" \
        $"Directory: $base_dir" \
        $"already exists.  Use the dkms remove function before trying to build again."

    # Read the conf file
    set_module_suffix "$kernelver"
    read_conf_or_die "$kernelver" "$arch"

    # Error out if build_exclude is set
    [[ $build_exclude ]] && die 9 \
        $" The dkms.conf for this module includes a BUILD_EXCLUSIVE directive which" \
        $"does not match this kernel/arch.  This indicates that it should not be built."

    # Error out if source_tree is basically empty (binary-only dkms tarball w/ --force check)
    (($(ls $dkms_tree/$module/$module_version/source | wc -l | awk {'print $1'}) < 2)) && die 8 \
        $"The directory $dkms_tree/$module/$module_version/source/" \
        $"does not appear to have module source located within it.  Build halted."

    prepare_kernel "$kernelver" "$arch"

    # Set up temporary build directory for build
    rm -rf "$dkms_tree/$module/$module_version/build"
    cp -a "$dkms_tree/$module/$module_version/source/" "$dkms_tree/$module/$module_version/build"

    cd "$dkms_tree/$module/$module_version/build"

    # Apply any patches
    for p in "${patch_array[@]}"; do
        [[ ! -e $dkms_tree/$module/$module_version/build/patches/$p ]] && \
            report_build_problem 5 \
            $" Patch $p as specified in dkms.conf cannot be" \
            $"found in $dkms_tree/$module/$module_version/build/patches/."
        invoke_command "patch -p1 < ./patches/$p" "applying patch $p" || \
            report_build_problem 6 $"Application of patch $p failed." \
            $"Check $dkms_tree/$module/$module_version/build/ for more information."
    done

    # Run the pre_build script
    run_build_script pre_build "$pre_build"
}

# Build our previously prepared source tree.  prepare_build must be called
# before calling this function.
do_build()
{
    local base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch"
    echo $""
    echo $"Building module:"

    invoke_command "$clean" "cleaning build area" background
    echo $"DKMS make.log for $module-$module_version for kernel $kernelver ($arch)" >> "$dkms_tree/$module/$module_version/build/make.log"
    date >> "$dkms_tree/$module/$module_version/build/make.log"
    local the_make_command="${make_command/#make/make -j$parallel_jobs KERNELRELEASE=$kernelver}"

    invoke_command "{ $the_make_command; } >> $dkms_tree/$module/$module_version/build/make.log 2>&1" "$the_make_command" background || \
        report_build_problem 10 $"Bad return status for module build on kernel: $kernelver ($arch)" \
        $"Consult $dkms_tree/$module/$module_version/build/make.log for more information."

    # Make sure all the modules built successfully
    for ((count=0; count < ${#built_module_name[@]}; count++)); do
        [[ -e ${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix ]] && continue
        report_build_problem 7 \
            $" Build of ${built_module_name[$count]}$module_uncompressed_suffix failed for: $kernelver ($arch)" \
            $"Consult the make.log in the build directory" \
            $"$dkms_tree/$module/$module_version/build/ for more information."
    done
    cd - >/dev/null

    # Build success, so create DKMS structure for a built module
    mkdir -p "$base_dir/log"
    [[ $kernel_config ]] && cp -f "$kernel_config" "$base_dir/log/"
    mv -f "$dkms_tree/$module/$module_version/build/make.log" "$base_dir/log/make.log" 2>/dev/null

    # Save a copy of the new module
    mkdir "$base_dir/module" >/dev/null
    for ((count=0; count < ${#built_module_name[@]}; count++)); do
        [[ ${strip[$count]} != no ]] && strip -g "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix"

        if [ -n "${sign_tool}" ]; then
            run_sign_tool "${sign_tool}" "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix"
        fi

        if [ "$module_compressed_suffix" = ".gz" ]; then
            gzip -9f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix"
        elif [ "$module_compressed_suffix" = ".xz" ]; then
            xz -f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix"
        fi
        cp -f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_suffix" \
            "$base_dir/module/${dest_module_name[$count]}$module_suffix" >/dev/null
    done

    # Run the post_build script
    run_build_script post_build "$post_build"
}

# Clean up after a build.
clean_build()
{
    # Run the clean commands
    cd "$dkms_tree/$module/$module_version/build"
    invoke_command "$clean" "cleaning build area" background
    cd - >/dev/null

    if [[ ! ( $(VER $kernelver) < $(VER 2.6.6) ) && \
       -d $kernel_source_dir && \
       ! -h $kernel_source_dir && \
       ! $ksourcedir_fromcli ]]; then
        echo $"Kernel cleanup unnecessary for this kernel.  Skipping..."
    elif [[ ! $no_clean_kernel ]]; then
        cd "$kernel_source_dir"
        [[ $kerneldoth_contents ]] || invoke_command "make mrproper" "cleaning kernel tree (make mrproper)" background
        [[ $config_contents ]] || echo "$config_contents" > .config
        [[ $kerneldoth_contents ]] && echo "$kerneldoth_contents" > /boot/kernel.h
        cd - >/dev/null
    fi

    # Clean the build directory
    rm -rf "$dkms_tree/$module/$module_version/build"
}

sign_build()
{
    [[ -x "$(command -v kmodsign)" && -d "/var/lib/shim-signed/mok/" ]] || return
    local base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch"
    if type update-secureboot-policy >/dev/null 2>&1; then
        echo $"Signing module:"
        SHIM_NOTRIGGER=y update-secureboot-policy --new-key
        for ko in `find "$base_dir/module/" -name "*.ko" -print`;
        do
            echo " - $ko"
            kmodsign sha512 \
                /var/lib/shim-signed/mok/MOK.priv \
                /var/lib/shim-signed/mok/MOK.der \
                "$ko"
        done
        update-secureboot-policy --enroll-key
    fi
}

build_module()
{
    prepare_build
    do_build
    sign_build
    clean_build
    echo $""
    echo $"DKMS: build completed."
}

# Force the installation of a module if this is listed
# in the files in $forced_modules_dir, if any
force_installation()
{
    forced_modules_dir="/usr/share/dkms/modules_to_force_install"
    to_force=""
    if [ -d $forced_modules_dir ]; then
        for elem in $forced_modules_dir/*; do
            if [ -e $elem ]; then
                to_force="$to_force $(cat $elem)"
            fi
        done

        for elem in $to_force; do
            if [ "${1}" = "${elem}" ]; then
                echo "force"
                return 0
            elif [ "${1}_version-override" = "${elem}" ]; then
                echo "version-override"
                return 0
            fi
        done
    fi
    return 1
}

# Install a previously built module
# There are huge swaths of code here that special-case for various distros.
# They should be split into their own functions.
install_module()
{
    # If the module has not been built, try to build it first.
    is_module_built "$module" "$module_version" "$kernelver" "$arch" || build_module
    local base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch"
    check_module_args install

    # Save the status of $force
    tmp_force="$force"

    # If the module is set to be force-installed
    local ret=$(force_installation $module)
    if [[ "$ret" == "force" ]];then
        force="true"
        echo "Forcing installation of $module"
    elif [[ "$ret" == "version-override" ]];then
        force_version_override="true"
        echo "Forcing version override of $module"
    fi
    # Make sure that kernel exists to install into
    [[ -e $install_tree/$kernelver ]] || die 6 \
        $"The directory $install_tree/$kernelver doesn't exist." \
        $"You cannot install a module onto a non-existant kernel."

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"

    # Check that its not already installed (kernel symlink)
    is_module_installed "$module" "$module_version" "$kernelver" "$arch" && die 5 \
        $"This module/version combo is already installed" \
        $"for kernel: $kernelver ($arch)"

    # If upgrading using rpm_safe_upgrade, go ahead and force the install
    # else we can wind up with the first half of an upgrade failing to install anything,
    # while the second half of the upgrade, the removal, then succeeds, leaving us with
    # nothing installed.
    [[ $rpm_safe_upgrade ]] && force="true"

    # Save the original_module if one exists, none have been saved before, and this is the first module for this kernel
    local lib_tree="$install_tree/$kernelver"
    local count
    for ((count=0; count < ${#built_module_name[@]}; count++)); do
    echo $""
    echo $"${dest_module_name[$count]}$module_suffix:"
    # Check this version against what is already in the kernel
    check_version_sanity "$kernelver" "$arch" "$obsolete_by" "${dest_module_name[$count]}" || continue

    if ((count == 0)) && ! run_build_script pre_install "$pre_install" && ! [[ $force ]]; then
        die 101 $"pre_install failed, aborting install." \
            $"You may override by specifying --force."
    fi
    local m=${dest_module_name[$count]}
    local installed_modules=$(find_module "$lib_tree" "$m" | grep -v "$lib_tree/kernel/")
    local module_count=${#installed_modules[@]}
    echo $" - Original module"
    local original_copy=$(compressed_or_uncompressed "$dkms_tree/$module/original_module/$kernelver/$arch" "$m")
    if [[ -L $dkms_tree/$module/kernel-$kernelver-$arch &&
        -n "$original_copy" ]]; then
        echo $"   - An original module was already stored during a previous install"
    elif ! [[ -L $dkms_tree/$module/kernel-$kernelver-$arch ]]; then
        local archive_pref1=$(compressed_or_uncompressed "$lib_tree/extra" "$m")
        local archive_pref2=$(compressed_or_uncompressed "$lib_tree/updates" "$m")
        local archive_pref3=$(compressed_or_uncompressed "$lib_tree${dest_module_location[$count]}" "$m")
        local archive_pref4=""
        ((module_count == 1)) && archive_pref4=${installed_modules[0]}
        local original_module=""
        local found_orginal=""
        for original_module in $archive_pref1 $archive_pref2 $archive_pref3 $archive_pref4; do
            [[ -f $original_module ]] || continue
            case "$running_distribution" in
                Debian* | Ubuntu* )
                    ;;
                *)
                echo $"   - Found $original_module"
                echo $"   - Storing in $dkms_tree/$module/original_module/$kernelver/$arch/"
                echo $"   - Archiving for uninstallation purposes"
                mkdir -p "$dkms_tree/$module/original_module/$kernelver/$arch"
                mv -f "$original_module" "$dkms_tree/$module/original_module/$kernelver/$arch/"
                ;;
            esac
            found_original="yes"
            break
        done
        if [[ ! $found_original ]] && ((module_count > 1)); then
            echo $"   - Multiple original modules exist but DKMS does not know which to pick"
            echo $"   - Due to the confusion, none will be considered during a later uninstall"
        elif [[ ! $found_original ]]; then
            echo $"   - No original module exists within this kernel"
        fi
    else
        echo $"   - This kernel never originally had a module by this name"
    fi

    if ((module_count > 1)); then
        echo $" - Multiple same named modules!"
        echo $"   - $module_count named $m$module_suffix in $lib_tree/"
        case "$running_distribution" in
            Debian* | Ubuntu* )
                ;;
            *)
                echo $"   - All instances of this module will now be stored for reference purposes ONLY"
                echo $"   - Storing in $dkms_tree/$module/original_module/$kernelver/$arch/collisions/"
                ;;
        esac
        for module_dup in $(find_module "$lib_tree" "$m"); do
            dup_tree="${module_dup#$lib_tree}";
            dup_name="${module_dup##*/}"
            dup_tree="${dup_tree/${dup_name}}"
            case "$running_distribution" in
                Debian* | Ubuntu* )
                    ;;
                *)
                   echo $"     - Stored $module_dup"
                   mkdir -p "$dkms_tree/$module/original_module/$kernelver/$arch/collisions/$dup_tree"
                   mv -f $module_dup "$dkms_tree/$module/original_module/$kernelver/$arch/collisions/$dup_tree"
                   ;;
            esac
        done
    fi

    # Copy module to its location
    echo $" - Installation"
    echo $"   - Installing to $install_tree/$kernelver${dest_module_location[$count]}/"
    mkdir -p $install_tree/$kernelver${dest_module_location[$count]}
    [[ $symlink_modules ]] && symlink="-s"
    local toinstall=$(compressed_or_uncompressed "$base_dir/module" "$m")
    cp -f $symlink "$toinstall" "$install_tree/$kernelver${dest_module_location[$count]}/${toinstall##*/}"

    done

    # Create the kernel-<kernelver> symlink to designate this version as active
    rm -f "$dkms_tree/$module/kernel-$kernelver-$arch" 2>/dev/null
    ln -s "$module_version/$kernelver/$arch" "$dkms_tree/$module/kernel-$kernelver-$arch" 2>/dev/null

    # Add to kabi-tracking
	if [ -z "$NO_WEAK_MODULES" ]; then
		if [[ ${weak_modules} ]]; then
			echo $"Adding any weak-modules"
			list_each_installed_module "$module" "$kernelver" "$arch" | ${weak_modules} ${weak_modules_no_initrd} --add-modules
		fi
	fi

    # Run the post_install script
    run_build_script post_install "$post_install"

    # Make modules.conf changes as necessary
    echo $""
    moduleconfig_add "$kernelver"
    etc_sysconfig_kernel_modify "add"

    invoke_command "do_depmod $kernelver" "depmod" background || {
        do_uninstall "$kernelver" "$arch"
        die 6 $"Problems with depmod detected.  Automatically uninstalling this module." \
            $"DKMS: Install Failed (depmod problems).  Module rolled back to built state."
            exit 6
    }

    # Make the newly installed modules available immediately
    find /sys/devices -name modalias -print0 | xargs -0 cat | xargs modprobe -a -b -q
    if [ -f /lib/systemd/system/systemd-modules-load.service ]; then
        systemctl restart systemd-modules-load.service
    fi

    # Do remake_initrd things (save old initrd)
    [[ $remake_initrd ]] && ! make_initrd "$kernelver" "$arch" backup && {
        do_uninstall "$kernelver" "$arch"
        die 7 $"Problems with mkinitrd detected.  Automatically uninstalling this module." \
            $"DKMS: Install Failed (mkinitrd problems).  Module rolled back to built state."
    }
    echo $""
    echo $"DKMS: install completed."

    # Restore the status of $force
    force="$tmp_force"
}

# List each kernel object that has been installed for a particular module.
list_each_installed_module()
{
    # $1 = module
    # $2 = kernel version
    # $3 = arch
    local count
    local real_dest_module_location
    local mod
    for ((count=0; count < ${#built_module_name[@]}; count++)); do
        real_dest_module_location="$(find_actual_dest_module_location $1 $count $2 $3)"
        mod=$(compressed_or_uncompressed "$install_tree/$2${real_dest_module_location}" "${dest_module_name[$count]}")
        echo "$mod"
    done
}

is_module_added() {
    [[ $1 && $2 ]] || return 1
    [[ -d $dkms_tree/$1/$2 ]] || return 2
    [[ -L $dkms_tree/$1/$2/source || -d $dkms_tree/$1/$2/source ]];
}

is_module_built() {
    [[ $1 && $2 && $3 && $4 ]] || return 1
    local d="$dkms_tree/$1/$2/$3/$4" m=''
    [[ -d $d/module ]] || return 1
    local default_conf="$dkms_tree/$1/$2/source/dkms.conf"
    # If a custom dkms.conf was specified use it, otherwise use the default one.
    local real_conf="${conf:-${default_conf}}"
    read_conf_or_die "$3" "$4" "$real_conf"
    set_module_suffix "$3"
    for m in "${dest_module_name[@]}"; do
        local t=$(compressed_or_uncompressed "$d/module" "$m")
        test -n "$t" || return 1
    done
}

# This assumes we have already checked to see if the module has been built.
_is_module_installed() {
    [[ $1 && $2 && $3 && $4 ]] || return 1
    local d="$dkms_tree/$1/$2/$3/$4"
    local k="$dkms_tree/$1/kernel-$3-$4"
    [[ -L $k && $(readlink -f $k) = $d ]]
}

# This does not.
is_module_installed() { is_module_built "$@" && _is_module_installed "$@"; }

maybe_add_module() (
    is_module_added "$1" "$2" && {
        echo $"Module $1/$2 already added."
        return 0
    }
    module="$1" module_version="$2" add_module
)

maybe_build_module() (
    is_module_built "$1" "$2" "$3" "$4" && {
        echo $"Module $1/$2 already built for kernel $3/4"
        return 0
    }
    module="$1" module_version="$2" kernelver="$3" arch="$4" build_module
)

maybe_install_module() (
    is_module_installed "$1" "$2" "$3" "$4" && {
        echo $"Module $1/$2 already installed on kernel $3/$4"
        return 0
    }
    module="$1" module_version="$2" kernelver="$3" arch="$4" install_module
)

build_modules() {
    local i=0
    for ((i=0; i < ${#kernelver[@]}; i++)); do
        maybe_build_module "$module" "$module_version" "${kernelver[$i]}" "${arch[$i]}"
    done
}

install_modules() {
    local i=0
    for ((i=0; i < ${#kernelver[@]}; i++)); do
        maybe_install_module "$module" "$module_version" "${kernelver[$i]}" "${arch[$i]}"
    done
}

check_module_exists() {
    is_module_added "$module" "$module_version" && return
    die 2 $"DKMS tree does not contain: $module-$module_version" \
        $"Build cannot continue without the proper tree."
}

possible_dest_module_locations()
{
    # $1 = count
    # There are two places an installed module may really be:
    # 1) "$install_tree/$kernelver/${dest_module_location[$count]}/${dest_module_name[$count]}$module_suffix"
    # 2) "$install_tree/$kernelver/${DEST_MODULE_LOCATION[$count]}/${dest_module_name[$count]}$module_suffix"
    # override_dest_module_location() is what controls whether or not they're the same.

    local location
    location[0]="${dest_module_location[$count]}"
    [[ ${DEST_MODULE_LOCATION[$count]} != ${dest_module_location[$count]} ]] && \
    location[1]="${DEST_MODULE_LOCATION[$count]}"

    echo "${location[@]}"
}

find_actual_dest_module_location()
{
    local module="$1"
    local count="$2"
    local kernelver="$3"
    local arch="$4"
    local locations="$(possible_dest_module_locations $count)"
    local l
    local dkms_owned
    local installed
    dkms_owned=$(compressed_or_uncompressed "${dkms_tree}/${module}/kernel-${kernelver}-${arch}/module" "${dest_module_name[$count]}")

    for l in $locations; do
        installed=$(compressed_or_uncompressed "${install_tree}/${kernelver}${l}" "${dest_module_name[${count}]}")
        if [[ -n "${installed}" ]] && diff "${dkms_owned}" "${installed}" > /dev/null 2>&1; then
            echo "${l}"
            return 0
        fi
    done

}

# Remove compiled DKMS modules from any kernels they are installed in.
do_uninstall()
{
    # $1 = kernel version
    # $2 = arch

    echo $""
    echo $"-------- Uninstall Beginning --------"
    echo $"Module:  $module"
    echo $"Version: $module_version"
    echo $"Kernel:  $1 ($2)"
    echo $"-------------------------------------"

    set_module_suffix "$1"

    # If kernel-<kernelver> symlink points to this module, check for original_module and put it back
    local was_active=""
    local kernel_symlink=$(readlink -f "$dkms_tree/$module/kernel-$1-$2")
    local real_dest_module_location
    if [[ $kernel_symlink = $dkms_tree/$module/$module_version/$1/$2 ]]; then
        was_active="true"
        echo $""
        echo $"Status: Before uninstall, this module version was ACTIVE on this kernel."
        # remove kabi-tracking if last instance removed
		if [ -z "$NO_WEAK_MODULES" ]; then
			if [[ ${weak_modules} ]] && (module_status_built $module $module_version |grep -q "installed"); then
				echo $"Removing any linked weak-modules"
				list_each_installed_module "$module" "$1" "$2" | ${weak_modules} ${weak_modules_no_initrd} --remove-modules
			fi
		fi

    for ((count=0; count < ${#built_module_name[@]}; count++)); do
        real_dest_module_location="$(find_actual_dest_module_location $module $count $1 $2)"
        echo $""
        echo $"${dest_module_name[$count]}$module_suffix:"
        echo $" - Uninstallation"
        echo $"   - Deleting from: $install_tree/$1${real_dest_module_location}/"
        rm -f "$install_tree/$1${real_dest_module_location}/${dest_module_name[$count]}$module_uncompressed_suffix"*
        dir_to_remove="${real_dest_module_location#/}"
        while [ "${dir_to_remove}" != "${dir_to_remove#/}" ]; do
            dir_to_remove="${dir_to_remove#/}"
        done
        (cd "$install_tree/$1" && rmdir -p --ignore-fail-on-non-empty "${dir_to_remove}" || true)
        echo $" - Original module"
        local origmod=$(compressed_or_uncompressed "$dkms_tree/$module/original_module/$1/$2" "${dest_module_name[$count]}")
        if [[ -n "$origmod" ]]; then
            case "$running_distribution" in
                Debian* | Ubuntu* )
                    ;;
                *)
                    echo $"   - Archived original module found in the DKMS tree"
                    echo $"   - Moving it to: $install_tree/$1${DEST_MODULE_LOCATION[$count]}/"
                    mkdir -p "$install_tree/$1${DEST_MODULE_LOCATION[$count]}/"
                    mv -f "$origmod" "$install_tree/$1${DEST_MODULE_LOCATION[$count]}/" 2>/dev/null
                    ;;
            esac
            else
            echo $"   - No original module was found for this module on this kernel."
            echo $"   - Use the dkms install command to reinstall any previous module version."

            # Remove modules_conf entries from /etc/modules.conf if remake_initrd is set or if this is last instance removed
            if [[ $remake_initrd ]] || (do_status $module $module_version | grep -q "installed"); then
                echo $""
                moduleconfig_remove "$1"
            fi
        fi
    done
    rm -f "$dkms_tree/$module/kernel-$1-$2"
    else
        echo $""
        echo $"Status: This module version was INACTIVE for this kernel."
    fi

    # Run the post_remove script
    run_build_script post_remove "$post_remove"

    # Run depmod because we changed /lib/modules
    invoke_command "do_depmod $1" "depmod" background

    # Do remake_initrd things (remake initrd)
    if [[ $remake_initrd && $was_active ]] && ! make_initrd "$1" "$2" ''; then
        warn $"There was a problem remaking your initrd.  You must manually remake it" \
            $"before booting into this kernel."
    fi

    # Delete the original_module if nothing for this kernel is installed anymore
    if [[ $was_active && -d $dkms_tree/$module/original_module/$1/$2 && ! -d $dkms_tree/$module/original_module/$1/$2/collisions ]]; then
        echo $""
        echo $"Removing original_module from DKMS tree for kernel $1 ($2)"
        rm -rf "$dkms_tree/$module/original_module/$1/$2" 2>/dev/null
        [[ $(find $dkms_tree/$module/original_module/$1/* -maxdepth 0 -type d 2>/dev/null) ]] || rm -rf "$dkms_tree/$module/original_module/$1"
    elif [[ $was_active && -d $dkms_tree/$module/original_module/$1/$2/collisions ]]; then
        echo $""
        echo $"Keeping directory $dkms_tree/$module/original_module/$1/$2/collisions/"
        echo $"for your reference purposes.  Your kernel originally contained multiple"
        echo $"same-named modules and this directory is now where these are located."
    fi
    [[ $(find $dkms_tree/$module/original_module/* -maxdepth 0 -type d 2>/dev/null) ]] || rm -rf "$dkms_tree/$module/original_module"

    # Re-add entries to modules.conf if this module/version is still installed on another kernel
    # But only do this if it was just ACTIVE on the kernel we just uninstalled from
    [[ $was_active && $remake_initrd ]] && do_status $module $module_version | grep -q "installed" && moduleconfig_add "$1"

    echo $""
    echo $"DKMS: uninstall completed."
}

# Check our preconditions, and then let do_install do all the hard work.
uninstall_module()
{
    # Check that the right arguments were passed
    check_module_args uninstall

    # Check that $module is in the dkms tree
    [[ -d $dkms_tree/$module ]] || die 2 \
        $"There are no instances of module: $module" \
        $"located in the DKMS tree."

    # Make sure that its installed in the first place
    [[ -d $dkms_tree/$module/$module_version ]] || die 3 \
        $"The module/version combo: $module-$module_version" \
        $"is not located in the DKMS tree."

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"

    # Only do stuff if module/module version is currently installed
    local kernel_symlink=$(readlink -f "$dkms_tree/$module/kernel-$kernelver-$arch")
    [[ $kernel_symlink = $dkms_tree/$module/$module_version/$kernelver/$arch ]] || die 5 \
        $"The module $module $module_version is not currently installed." \
        $"This module is not currently ACTIVE for kernel $kernelver ($arch)."
    do_uninstall "$kernelver" "$arch"
}

# Unregister a DKMS module.  This uninstalls any installed modules along the way.
remove_module()
{
    # Check that the right arguments were passed
    if [[ ! ($module && $module_version) || $kernels_arches_default  ]]; then
        die 1 $"Invalid number of parameters passed." \
            $"Usage: remove <module>/<module-version> --all" \
            $"   or: remove <module>/<module-version> -k <kernel-version>"
    fi

    # Check that $module is in the dkms tree
    if ! [[ -d $dkms_tree/$module/$module_version ]]; then
        die 3 $"There are no instances of module: $module" \
            $"$module_version located in the DKMS tree."
    fi

    local i
    for ((i=0; i < ${#kernelver[@]}; i++)); do
        # Make sure its there first before removing
        if ! [[ -d $dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]} ]]; then
            die 4 $"There is no instance of $module $module_version" \
            $"for kernel ${kernelver[$i]} (${arch[$i]}) located in the DKMS tree."
        fi

        # Do --rpm_safe_upgrade check (exit out and don't do remove if inter-release RPM upgrade scenario occurs)
        if [[ $rpm_safe_upgrade ]]; then
            local pppid=$(awk '/PPid:/ {print $2}' /proc/$PPID/status)
            local time_stamp=$(ps -o lstart --no-headers -p $pppid 2>/dev/null)
            for lock_file in $tmp_location/dkms_rpm_safe_upgrade_lock.$pppid.*; do
                [[ -f $lock_file ]] || continue
                lock_head=$(head -n 1 $lock_file 2>/dev/null)
                lock_tail=$(tail -n 1 $lock_file 2>/dev/null)
                [[ $lock_head = $module-$module_version && $time_stamp && $lock_tail = $time_stamp ]] || continue
                rm -f $lock_file
                die 0 $"DKMS: Remove cancelled because --rpm_safe_upgrade scenario detected."
            done
        fi

        # Read the conf file
        read_conf_or_die "${kernelver[$i]}" "${arch[$i]}"

        do_uninstall "${kernelver[$i]}" "${arch[$i]}"

        # Delete the $kernel_version/$arch_used part of the tree
        rm -rf "$dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]}"
        [[ $(find $dkms_tree/$module/$module_version/${kernelver[$i]}/* -maxdepth 0 -type d 2>/dev/null) ]] || \
            rm -rf "$dkms_tree/$module/$module_version/${kernelver[$i]}"
    done

    # Delete the $module_version part of the tree if no other $module_version/$kernel_version dirs exist
    if ! find $dkms_tree/$module/$module_version/* -maxdepth 0 -type d 2>/dev/null | egrep -qv "(build|tarball|driver_disk|rpm|deb|source)$"; then
        echo $""
        echo $"------------------------------"
        echo $"Deleting module version: $module_version"
        echo $"completely from the DKMS tree."
        echo $"------------------------------"
        rm -rf "$dkms_tree/$module/$module_version"
        echo $"Done."
    fi

    # Get rid of any remnant directories if necessary
    if (($(ls "$dkms_tree/$module" | wc -w | awk '{print $1}') == 0)); then
        rm -rf "$dkms_tree/$module" 2>/dev/null

        # Its now safe to completely remove references in /etc/sysconfig/kernel for SuSE
        etc_sysconfig_kernel_modify "delete"
    fi
}

# Given a kernel object, figure out which DKMS module it is from.
find_module_from_ko()
{
    local ko="$1"
    local basename_ko="${ko##*/}"
    local module
    local kernellink

    for kernellink in "$dkms_tree"/*/kernel-*; do
        [[ -L $kernellink ]] || continue
        module=${kernellink#$dkms_tree/}
        module=${module%/kernel-*}
        diff "$kernellink/module/${basename_ko}" "${ko}" >/dev/null 2>&1 || continue
        rest=$(readlink $kernellink)
        echo "$module/$rest"
        return 0
    done
    return 1
}

# Check to see if modules meeting the passed parameters are weak-installed.
# This function's calling convention is different from the usual DKMS status
# checking functions -- the kernel version we usually have is the one we are currently
# running on, not necessarily the one we compiled the module for.
module_status_weak() {
    # $1 = module, $2 = module version, $3 = kernel version weak installed to,
    # $4 = kernel arch, $5 = kernel version built for
	[ -z "$NO_WEAK_MODULES" ] || return 1
    [[ $weak_modules ]] || return 1
    local m v k a kern weak_ko mod installed_ko f ret=1 oifs=$IFS
    local -A already_found
    for weak_ko in "$install_tree/"*/weak-updates/*; do
        [[ -e $weak_ko ]] || continue
        [[ -L $weak_ko ]] && installed_ko="$(readlink -f "$weak_ko")" || continue
        IFS=/ read m v k a < <(IFS=$oifs find_module_from_ko "$weak_ko") || continue
        kern=${weak_ko#$install_tree/}
        kern=${kern%/weak-updates/*}
        [[ $m = ${1:-*} && $v = ${2:-*} && $k = ${5:-*} && $a = ${4:-*} && $kern = ${3:-*} ]] || continue
	already_found[$m/$v/$kern/$a/$k]+=${weak_ko##*/}" "
    done
    # Check to see that all ko's are present for each module
    for mod in ${!already_found[@]}; do
	IFS=/ read m v k a kern <<< "$mod"
	# ensure each module is weak linked
	for installed_ko in $(find $dkms_tree/$m/$v/$kern/$a/module -type f); do
	    [[ ${already_found[$mod]} != *"$installed_ko"* ]] && continue 2
	done
        ret=0
        echo "installed-weak $mod"
    done
    return $ret
}

# Print the requested status lines for weak-installed modules.
do_status_weak()
{
    local mvka m v k a kern status
    while read status mvka; do
        IFS=/ read m v k a kern <<< "$mvka"
        echo "$m, $v, $k, $a: installed-weak from $kern"
    done < <(module_status_weak "$@")
}

# Spit out all the extra status information that people running DKMS are
# interested in, but that the DKMS internals do not usually care about.
module_status_built_extra() (
    set_module_suffix "$3"
    read_conf "$3" "$4" "$dkms_tree/$1/$2/source/dkms.conf"
    [[ -d $dkms_tree/$1/original_module/$3/$4 ]] && echo -n " (original_module exists)"
    for ((count=0; count < ${#dest_module_name[@]}; count++)); do
        tree_mod=$(compressed_or_uncompressed "$dkms_tree/$1/$2/$3/$4/module" "${dest_module_name[$count]}")
        if ! [[ -n "$tree_mod" ]]; then
            echo -n " (WARNING! Missing some built modules!)"
        elif _is_module_installed "$@"; then
            real_dest="$(find_actual_dest_module_location "$1" $count "$3" "$4")"
            real_dest_mod=$(compressed_or_uncompressed "$install_tree/$3${real_dest}" "${dest_module_name[$count]}")
            if ! diff -q "$tree_mod" "$real_dest_mod" >/dev/null 2>&1; then
                echo -n " (WARNING! Diff between built and installed module!)"
            fi
        fi
    done
)

# Return a list of all the modules that are either built or installed.
# This and module_status do some juggling of $IFS to ensure that
# we do not get word splitting where it would be inconvienent.
module_status_built() {
    local ret=1 directory ka k a state oifs="$IFS" IFS=''
    for directory in "$dkms_tree/$1/$2/"${3:-+([0-9]).*}/${4:-*}; do
        IFS="$oifs"
        ka="${directory#$dkms_tree/$1/$2/}"
        k="${ka%/*}"
        a="${ka#*/}"
        is_module_built "$1" "$2" "$k" "$a" || continue
        ret=0
        state="built"
        _is_module_installed "$1" "$2" "$k" "$a" && state="installed"
        echo "$state $1/$2/$k/$a"
        IFS=''
    done
    IFS="$oifs"
    return $ret
}

# Return the status of all modules that have been added, built, or installed.
module_status() {
    local oifs="$IFS" IFS='' mv m v directory ret=1
    for directory in "$dkms_tree/"${1:-*}/${2:-*}; do
        IFS="$oifs"
        mv="${directory#$dkms_tree/}"
        m="${mv%/*}"
        v="${mv#*/}"
        is_module_added "$m" "$v" || continue
        ret=0
        module_status_built "$m" "$v" "$3" "$4" || echo "added $m/$v"
        IFS=''
    done
    IFS="$oifs"
    return $ret
}

# Print out the status in the format that people who call DKMS expect.
# Internal callers should use the module_status functions, as their output
# is easier to parse.
do_status() {
    local status mvka m v k a
    while read status mvka; do
        IFS=/ read m v k a <<< "$mvka"
        case $status in
            added)
                echo "$m, $v: $status"
                ;;
            built|installed)
                echo -n "$m, $v, $k, $a: $status"
                module_status_built_extra "$m" "$v" "$k" "$a"
                echo
                ;;
        esac
    done < <(module_status "$@")
}

# Show all our status in the format that external callers expect, even
# though it is slightly harder to parse.
show_status()
{
    local j state_array
    if ((${#kernelver[@]} == 0)); then
        do_status "$module" "$module_version" "$kernelver" "$arch"
        do_status_weak "$module" "$module_version" "$kernelver" "$arch"
    else
    for ((j=0; j < ${#kernelver[@]}; j++)); do
        do_status "$module" "$module_version" "${kernelver[$j]}" "${arch[$j]}"
        do_status_weak "$module" "$module_version" "${kernelver[$j]}" "${arch[$j]}"
    done
    fi
}

create_temporary_trees()
{
    [[ $module || $module_version || ! -r dkms.conf ]] && return 0

    . dkms.conf
    module="$PACKAGE_NAME"
    module_version="$PACKAGE_VERSION"

    source_tree=$(mktemp_or_die -d)
    dkms_tree=$(mktemp_or_die -d)

    local source_tree_dir="$source_tree/$PACKAGE_NAME-$PACKAGE_VERSION"
    mkdir -p "$source_tree_dir"
    cp -a * "$source_tree_dir" # intentionally skip .git or .hg
    add_module
    temporary_trees_del_command="rm -rf $source_tree $dkms_tree"
}

delete_temporary_trees()
{
    [[ $temporary_trees_del_command ]] || return 0
    $temporary_trees_del_command
    module=
    module_version=
    source_tree=
    dkms_tree=
    temporary_trees_del_command=
}

in_temporary_trees()
{
    [[ $temporary_trees_del_command ]]
}

media_valid()
{
    local mrx='^(floppy|iso|tar)$'
    [[ $media =~ $mrx ]]
}

make_driver_disk_floppy()
{
    local image_name="$1"
    local source_dir="$2"
    local file
    local fs='ext2'
    [[ $distro = redhat* ]] && fs='vfat'

    rm -f "$image_name"
    invoke_command "dd if=/dev/zero of=$image_name bs=$(($size/20))k count=20" "making a blank floppy image" background
    case $fs in
        vfat)
            invoke_command "mkdosfs $image_name" "mkdosfs" background
            ;;
        ext2)
            invoke_command "mke2fs -F $image_name" "mke2fs" background
            ;;
    esac

    local mntdir=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    invoke_command "mount -o loop -t $fs $image_name $mntdir >/dev/null 2>&1" "loopback mounting disk image"
    [[ -d $mntdir/lost+found ]] && rmdir "$mntdir/lost+found"
    invoke_command "cp -r $source_dir/* $mntdir/" "  copying files to floppy disk image"
    invoke_command "umount $mntdir" "unmounting disk image"
    rm -rf "$mntdir"
}

make_driver_disk_isotar()
{
    local type="$1"
    local image_name="$2"
    local source_dir="$3"
    local file
    case $type in
        iso)
            invoke_command "mkisofs -v -r -J -pad -V $module -o $image_name ." "mkisofs" background
            ;;
        tar)
            invoke_command "tar cvf $image_name ." "tar" background
            ;;
    esac
}

make_driver_disk_media()
{
    echo "Copying files $2"

    case $media in
        floppy*)
            make_driver_disk_floppy "$1" "$2"
            ;;
        iso*)
            make_driver_disk_isotar "iso" "$1" "$2"
            ;;
        tar*)
            make_driver_disk_isotar "tar" "$1" "$2"
            ;;
    esac
}

driver_disk_suffix()
{
    case $media in
    floppy*)
        echo "img"
        ;;
    iso*)
        echo "iso"
        ;;
    tar*)
        echo "tar"
        ;;
    esac
}

make_driver_disk()
{
    # Check that the right arguments were passed
    if ! [[ $module && $module_version && $distro && $kernelver ]]; then
        die 1 $"Invalid number of parameters passed." \
        $"Usage: mkdriverdisk <module>/<module-version> -d <distro> -k <kernelver> [--media floppy|iso|tar]"
    fi

    # Default to floppy media
    [[ $media ]] || media="floppy"
    if ! media_valid; then
        die 1 $"Media $media is invalid." \
            $"Usage: mkdriverdisk <module>/<module-version> -d <distro> -k <kernelver> [--media floppy|iso|tar]"
    fi

    # Check that source symlink works
    check_module_exists

    # Confirm that distro is supported
    case $distro in
        redhat3 | suse | UnitedLinux | ubuntu)
            ;;
        *)
            die 3 $"Invalid distro argument. Currently, the distros" \
                $"supported are: redhat3, suse, UnitedLinux, ubuntu"
            ;;
    esac

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"

    case $distro in
        redhat*)
            make_redhat3_driver_disk
            ;;
        ubuntu)
            make_ubuntu_driver_disk
            ;;
        *)
            make_suse_driver_disk
            ;;
    esac
}

find_external_dependencies()
{
    local mod count i
    local -a deps

    # Find all module dependencies
    for ((count=0; count < ${#dest_module_name[@]}; count++)); do
        for ((i=0; i < ${#kernelver[@]}; i++)); do
            set_module_suffix "${kernelver[$i]}"
            mod="$dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]}/module/${dest_module_name[$count]}$module_suffix"
            deps=(${deps[@]} $(modinfo "$mod" | sed -n 's/,/ /g; s/^depends: *//p'))
        done
    done

    # Prune internally satisfied dependencies

    for ((i=0; i < ${#deps[@]}; i++)); do
        for mod in ${dest_module_name[@]}; do
            [[ ${deps[i]} = $mod ]] && deps[i]=
        done
    done

    for dep in "${deps[@]}"; do
        echo $dep
    done | sort -u
}

make_suse_driver_disk()
{
    [[ $release ]] || die 3 \
        $"Invalid number of parameters passed for suse/UnitedLinux driver disk." \
        $"Usage: mkdriverdisk <module>/<module-version> -d <distro> -k <kernelver>" \
        $"                    -r <release-number>"

    local driver_disk_dir=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    local suffix="$(driver_disk_suffix)"
    local image_dir="$dkms_tree/$module/$module_version/driver_disk"
    local image_name="$module-$module_version-$distro-$release-dd.$suffix"

    echo $""
    echo $"Creating driver disk:"

    local deps="$(find_external_dependencies)"

    local offset=0
    # reserve a place for dependencies
    [[ ${deps[@]} ]] && offset=1
    
    local count
    for ((count=0; count < ${#dest_module_name[@]}; count++)); do
    local i
    local topdir=$(printf "%02d" $(($count+1+offset)))
    for ((i=0; i < ${#kernelver[@]}; i++)); do
        set_module_suffix "${kernelver[$i]}"
        local srcdir=$dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]}/module
        local srcpath=$(compressed_or_uncompressed "$srcdir" "${dest_module_name[$count]}")
        if ! [[ -n "$srcpath" ]]; then
            rm -rf $temp_dir_name
            die 5 \
                $"Cannot find module ${dest_module_name[$count]}$module_suffix for kernel ${kernelver[$i]} (${arch[$i]})." \
                $"Module/version must be in built state before making a driver disk."
        fi
        # FIXME: add check for KMP binary RPMs to include in the driver disk
        suse_arch=${arch[$i]}
        case $suse_arch in
            i?86)
                suse_arch=i386
                ;;
        esac

        echo "Marking ${kernelver[$i]}/${arch[$i]}/modules/${dest_module_name[$count]}$module_suffix..."
        mkdir -p "$driver_disk_dir/$topdir/$suse_arch-$release/install/lib/modules/${kernelver[$i]}${dest_module_location[$count]}"
        cp "$srcpath" "$driver_disk_dir/$topdir/$suse_arch-$release/install/lib/modules/${kernelver[$i]}${dest_module_location[$count]}/"

        case ${kernelver[$i]} in
        *-default)
            mkdir -p "$driver_disk_dir/$topdir/$suse_arch-$release/modules/"
            cp "$srcpath" "$driver_disk_dir/$topdir/$suse_arch-$release/modules/"
            ;;
        esac

        # Create directory for dependency information
        [[ ${deps[@]} ]] && mkdir -p "$driver_disk_dir/01/linux/$distro/$suse_arch-$release/modules"

    done

    # ---
    for arch_release in $(find $driver_disk_dir/$topdir -maxdepth 1 -mindepth 1 -type d | sed "s#$driver_disk_dir\/$topdir\/##"); do
        cd "$driver_disk_dir/$topdir/$arch_release/install/"
        invoke_command "tar cvzf update.tar.gz lib/" "making update.tar.gz for $arch_release" background
        cd - >/dev/null

        mkdir -p "$driver_disk_dir/$topdir/linux/$distro/$arch_release/install"
        mkdir -p "$driver_disk_dir/$topdir/linux/$distro/$arch_release/modules"

        echo $"  copying update.tar.gz for $arch_release to disk image..."
        cp -f "$driver_disk_dir/$topdir/$arch_release/install/update.tar.gz" "$driver_disk_dir/$topdir/linux/$distro/$arch_release/install/"

        postkernels=
        archtest=${arch_release/-*}
        for ((i=0; i<${#kernelver[@]}; i++)); do
            [[ ${arch[$i]} = ${archtest} ]] && postkernels="${postkernels} ${kernelver[$i]}"
        done

        if [[ ${postkernels} ]]; then
            dstfile="$driver_disk_dir/$topdir/linux/$distro/$arch_release/install/update.post"
            echo $"  creating update.post for $arch_release..."
            (cat << EOF
#!/bin/sh

kernlist="${postkernels}"

for kernel in \${kernlist}; do
    if [ -e /boot/System.map-\${kernel} ]; then
    depmod -a -F /boot/System.map-\${kernel} \${kernel}
    fi
done

EOF
            ) > ${dstfile}
            chmod a+x ${dstfile}
            fi

            if [[ -d $driver_disk_dir/$topdir/$arch_release/modules/ ]]; then
                echo $"  copying kernel modules for installation kernel to disk image..."
                cp -f $driver_disk_dir/$topdir/$arch_release/modules/* $driver_disk_dir/$topdir/linux/$distro/$arch_release/modules/ 2>/dev/null
            else
                warn $"No kernel modules found for -default kernel."
            fi

            rm -fr "$driver_disk_dir/$topdir/$arch_release"
        done
    done

    local dir
    if [[ ${deps[@]} ]]; then
        for dir in "$driver_disk_dir/01/linux/$distro/"*"/modules"; do
            for dep in "${deps[@]}"; do
                echo $dep >> "$dir/module.order"
            done
        done
    fi

    # FIXME: add suse-equivalent rpms/ directory, copy in KMP RPMs, run createrepo --pretty

    mkdir -p "$image_dir"
    rm -f "$image_dir/$image_name"
    cd "$driver_disk_dir"
    make_driver_disk_media "$image_dir/$image_name" "$driver_disk_dir"
    cd - >/dev/null
    rm -rf "$driver_disk_dir"

    echo $""
    echo $"Disk image location: $dkms_tree/$module/$module_version/driver_disk/$image_name"
    echo $""
    echo $"DKMS: mkdriverdisk completed."
}

make_ubuntu_driver_disk()
{
    local suffix="$(driver_disk_suffix)"
    local image_dir="$dkms_tree/$module/$module_version/driver_disk"
    local image_name="$module-$module_version-$distro-dd.$suffix"

    local tempdir=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)

    # Check that the dh_make command is present
    if ! which dpkg-deb >/dev/null 2>&1 ; then
        die 1 $"dpkg-deb not present." \
            $"Install the dpkg-dev package."
    fi

    local i
    for ((i=0; i < ${#kernelver[@]}; i++)); do
        set_module_suffix "${kernelver[$i]}"
        # Driver disks only recognize i386 as package arch
        local karch=${arch[$i]/i?86/i386}
        local kvers=${kernelver[$i]/-/_}; kvers=${kvers%%_*}
        # ubuntu-drivers/<kver>/*_<debarch>.deb
        local dd_prefix="ubuntu-drivers/$kvers"
        local dd_suffix="_${karch}.deb"
        maybe_build_module "$module" "$module_version" "${kernelver[$i]}" "${arch[$i]}" || {
            rm -rf "$tempdir"
            die 5 $"Unable to build $module/$module_version for Ubuntu driver disk."
        }
        mkdir -p "$tempdir/$dd_prefix"
        local deb_dir="$tempdir/$dd_prefix/debian"
        local deb_lib_dir="$deb_dir/lib/modules/${kernelver[$i]}/updates/dkms"
        mkdir -p "$deb_lib_dir"
        cp "$dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]}/module/"*"$module_uncompressed_suffix"* "$deb_lib_dir"
        pushd "$deb_dir" > /dev/null 2>&1
        mkdir DEBIAN
        cat > DEBIAN/control <<EOF
Package: ${module}-modules-${kernelver[$i]}
Version: ${module_version}-1
Section: misc
Priority: optional
Architecture: $karch
Depends:
Maintainer: DKMS <dkms-devel@dell.com>
Description: DKMS packaged binary driver update
 DKMS automagically generated debian package for
 driver update disks, used with Ubuntu installation
 programs (such as Ubiquity).
EOF

       # Generate the DEBIAN/preinst file.
       # This is tricky as we need some parts evaluated now
       # and some parts evaluated at runtime
cat >DEBIAN/preinst <<EOF
#!/bin/bash
[[ \$(uname -r) = ${kernelver[$i]} ]] || exit 1
exit 0
EOF
       chmod 0775 DEBIAN/preinst
       cd "$tempdir/$dd_prefix"
       dpkg-deb --build debian
       mv debian.deb "${module}_${module_version}-${kernelver[$i]}${dd_suffix}"
       rm -rf debian
       popd > /dev/null 2>&1
   done

   echo "Copying source..."
   mkdir -p "$tempdir/ubuntu"
   cp -ar "$source_tree/$module-$module_version" "$tempdir/ubuntu/"

   mkdir -p "$image_dir"
   rm -f "$image_dir/$image_name"
   cd "$tempdir"
   make_driver_disk_media "$image_dir/$image_name" "$tempdir"
   cd - >/dev/null
   rm -rf "$tempdir"
   echo $""
   echo $"Disk image location: $dkms_tree/$module/$module_version/driver_disk/$image_name"
   echo $""
   echo $"DKMS: mkdriverdisk completed."
}

make_tarball()
{
    make_common_test "mktarball"

    # Check for dkms_dbversion
    if ! [[ -e $dkms_tree/dkms_dbversion ]]; then
        echo $"" >&2
        echo $"Could not find the file $dkms_tree/dkms_dbversion." >&2
        echo $"Creating w/ default contents." >&2
        echo "2.0.0" > $dkms_tree/dkms_dbversion
    fi

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"

    temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    mkdir -p $temp_dir_name/dkms_main_tree

    if [[ $source_only ]]; then
    kernel_version_list="source-only"
    else
    local i
    for ((i=0; i<${#kernelver[@]}; i++)); do
        if ! [[ -d $dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]} ]]; then
            rm -rf "$temp_dir_name" 2>/dev/null
            die 6  $"No modules built for ${kernelver[$i]} (${arch[$i]})." \
                $"Modules must already be in the built state before using mktarball."
        fi

        set_module_suffix "${kernelver[$i]}"

        echo "Marking modules for ${kernelver[$i]} (${arch[$i]}) for archiving..."
        if [[ ! $kernel_version_list ]]; then
            kernel_version_list="kernel${kernelver[$i]}-${arch[$i]}"
        else
            kernel_version_list="${kernel_version_list}-kernel${kernelver[$i]}-${arch[$i]}"
        fi
        mkdir -p "$temp_dir_name/dkms_main_tree/${kernelver[$i]}/${arch[$i]}"
        cp -rf "$dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]}" "$temp_dir_name/dkms_main_tree/${kernelver[$i]}"
    done
    fi

    # Store the dkms_dbversion in the tarball
    cp -f "$dkms_tree/dkms_dbversion" "$temp_dir_name/dkms_main_tree/"

    # Copy the source_tree or make special binaries-only structure
    if [[ $binaries_only ]]; then
        echo $""
        echo $"Creating tarball structure to specifically accomodate binaries."
        mkdir $temp_dir_name/dkms_binaries_only
        echo "$module" > $temp_dir_name/dkms_binaries_only/PACKAGE_NAME
        echo "$module_version" > $temp_dir_name/dkms_binaries_only/PACKAGE_VERSION
        [[ ! $conf ]] && conf="$dkms_tree/$module/$module_version/source/dkms.conf"
        cp -f $conf $temp_dir_name/dkms_binaries_only/ 2>/dev/null
    else
        echo $""
        echo $"Marking $dkms_tree/$module/$module_version/source for archiving..."
        mkdir -p $temp_dir_name/dkms_source_tree
        cp -rf $dkms_tree/$module/$module_version/source/* $temp_dir_name/dkms_source_tree
    fi

    if (( $(echo $kernel_version_list | wc -m | awk {'print $1'}) > 200 )); then
        kernel_version_list="manykernels"
    fi

    local tarball_name="$module-$module_version-$kernel_version_list.dkms.tar.gz"
    local tarball_dest="$dkms_tree/$module/$module_version/tarball/"

    # Die if we will not be able to create the tarball due to permissions.
    if [[ $archive_location ]]; then
        tarball_name="${archive_location##*/}"
        if [[ ${archive_location%/*} != $archive_location && \
            -d ${archive_location%/*} && -w ${archive_location%/*} ]]; then
            tarball_dest="${archive_location%/*}"
        elif [[ ${archive_location%/*} != $archive_location ]] && ! mkdir -p $tarball_dest; then
            die 9 $"Will not be able to create $archive_location due to a permissions problem."
        fi
    fi
    if [ ! -d $tarball_dest ]; then
        mkdir -p "$dkms_tree/$module/$module_version/tarball/"
    fi

    echo $""
    echo $"Tarball location: $tarball_dest/$tarball_name"

    local tarball_ext=${tarball_name##*.}
    [[ $tarball_ext = tar ]] || tarball_name=${tarball_name%.$tarball_ext}

    # Make the tarball
    cd $temp_dir_name
    if tar -cf $temp_dir_name/$tarball_name ./* 2>/dev/null; then
        cd - >/dev/null
        echo $""
        mv -f "$temp_dir_name/$tarball_name" "$tarball_dest/$tarball_name"
        rm -rf $temp_dir_name
    else
        cd - >/dev/null
        rm -rf $temp_dir_name
        die 6 $"Failed to make tarball."
    fi
    case $tarball_ext in
        gz)
            gzip -f -9 "$tarball_dest/$tarball_name"
            ;;
        bz2)
            bzip2 -f -9 "$tarball_dest/$tarball_name"
            ;;
        xz)
            xz -f -9 "$tarball_dest/$tarball_name"
            ;;
    esac
    echo $""
    echo $"DKMS: mktarball completed."
}

# A tiny helper function to make sure dkms.conf describes a valid package.
get_pkginfo_from_conf() {
    [[ -f $1 && $1 = *dkms.conf ]] || return
    read_conf_or_die "$kernelver" "$arch" "$1"
    [[ $PACKAGE_NAME && $PACKAGE_VERSION ]]
}

# Unpack a DKMS tarball from a few different supported formats.
# We expect $archive_location to have been passed either as a raw argument or
# with --archive.
load_tarball()
{
    # Error out if $archive_location does not exist
    if [[ ! -e $archive_location ]]; then
        die 2 $"$archive_location does not exist."
    fi

    # If it is an .rpm file. install it with rpm, run an autoinstall, and then exit.
    if [[ $archive_location = *.rpm ]]; then
       if rpm -Uvh "$archive_location"; then
           autoinstall
           exit $?
       else
           die 9 $"Unable to install $archive_location using rpm." \
               $"Check to ensure that your system can install .rpm files."
       fi
    fi

    # Figure out what kind of archive it is (tar.gz, tar, tar.bz, tar.xz, etc)
    # Note that this does not depend on the extensions being correct.
    local tar_options=""
    for xpand in gzip bzip xz; do
        $xpand -t $archive_location 2>/dev/null || continue
        case $xpand in
            gzip)
                tar_options=z
                ;;
            bzip2)
                tar_options=j
                ;;
            xz)
                tar_options=J
                ;;
        esac
        break
    done

    # Untar it into $tmp_location
    local temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    trap 'rm -rf $temp_dir_name' EXIT
    tar -${tar_options}xf $archive_location -C $temp_dir_name

    if [[ ! $temp_dir_name/dkms_main_tree ]]; then
    # Tarball was not generated from mktarball.
    # Just find the dkms.conf file and load the source.
    conf=$(find $temp_dir_name/ -name dkms.conf 2>/dev/null | head -n 1)
    if [[ ! $conf ]]; then
        rm -rf $temp_dir_name
        die 3 $"Tarball does not appear to be a correctly formed" \
            $"DKMS archive. No dkms.conf found within it."
    fi
    add_source_tree "${conf%dkms.conf}"
    return
    fi

    # Check that dkms_dbversion is not a future version
    # As far as I can tell, the only reason we bother with this is for detecting
    # whether we have arch support or not, which we can also determine by examining
    # the structure of the tarball.
    db_from_tarball=$(cat $temp_dir_name/dkms_main_tree/dkms_dbversion 2>/dev/null)
    db_from_dkms=$(cat $dkms_tree/dkms_dbversion 2>/dev/null)
    if [[ $db_from_tarball && $db_from_dkms && $(VER "$db_from_tarball") > $(VER "$db_from_dkms") ]]; then
        die 9 \
            $"The tarball you are trying to load indicates it is database version" \
            $"$db_from_tarball.  This version of DKMS only supports $db_from_dkms or lower."
    fi

    # Make sure its a sane tarball. Sane ones will have one of the two
    # directories we test for.
    for loc in dkms_source_tree dkms_binaries_only ''; do
    if [[ ! $loc ]]; then
        die 7 $"No valid dkms.conf in dkms_source_tree or dkms_binaries_only." \
            $"$archive_location is not a valid DKMS tarball."
    fi
    local conf="$temp_dir_name/$loc/dkms.conf"
    [[ -f $conf ]] || continue
    if ! get_pkginfo_from_conf "$conf"; then
        echo >&2
        echo $"Malformed dkms.conf, refusing to load." >&2
        continue
    fi
    if is_module_added "$PACKAGE_NAME" "$PACKAGE_VERSION" && \
        [[ ! $force ]]; then
        die 8  $"$PACKAGE_NAME-$PACKAGE_VERSION is already added!" \
        $"Aborting."
    fi
    module="$PACKAGE_NAME"; module_version="$PACKAGE_VERSION"
    echo $""
    echo $"Loading tarball for $module-$module_version"
    case $loc in
        dkms_source_tree)
            add_source_tree "$temp_dir_name/dkms_source_tree"
            ;;
        dkms_binaries_only)
            #if there is a source tree on the system already, don't build a binaries stub
            if [[ ! -d $source_tree/$module-$module_version ]]; then
                echo $"Creating $dkms_tree/$module/$module_version/source"
                mkdir -p "$dkms_tree/$module/$module_version/source"
                echo $"Copying dkms.conf to $dkms_tree/$module/$module_version/source..."
                cp -rf "$temp_dir_name/dkms_binaries_only/dkms.conf" "$dkms_tree/$module/$module_version/source"
            fi
            ;;
        *)
            die 8 $"$FUNCNAME:$LINENO: Cannot happen." \
                $"Report this error to dkms-devel@dell.com";;
    esac
    break
    done

    # At this point, the source has been copied to the appropriate location
    # and registered with dkms, or a binary-only config has been noted.
    # Now, add any included precompiled modules.

    # Is tarball from before DKMS 2.0 (prior to arch support)
    if [[ ! -e $temp_dir_name/dkms_main_tree/dkms_dbversion ]]; then
        [[ $loc = dkms_binaries_only ]] && rm -rf "$dkms_tree/$module/$module_version/source"
        die 10 $" This tarball was created with dkms < 2.0 and contains" \
            $"no arch info. DKMS is refusing to install precompiled modules."
    fi

    # Load precompiled modules.
    for directory in "$temp_dir_name/dkms_main_tree"/*/*; do
        [[ -d $directory ]] || continue
        kernel_arch_to_load=${directory/*dkms_main_tree\/}
        dkms_dir_location="$dkms_tree/$module/$module_version/$kernel_arch_to_load"
        if [[ -d $dkms_dir_location && ! $force ]]; then
            warn $"$dkms_dir_location already exists.  Skipping..."
        else
            echo $"Loading $dkms_dir_location..."
            rm -rf $dkms_dir_location
            mkdir -p $dkms_dir_location
            cp -rf $directory/* $dkms_dir_location/
        fi
    done

    echo $""
    echo $"DKMS: ldtarball completed."
    [[ $loc != dkms_binaries_only ]] || [[ -d $source_tree/$module-$module_version ]]
}

run_match()
{
    set_kernel_source_dir "$kernelver"

    # Error if $template_kernel is unset
    if [[ ! $template_kernel ]]; then
        die 1 $"Invalid number of parameters passed." \
            $"Usage: match --templatekernel=<kernel-version> -k <kernel-version>" \
            $"   or: match --templatekernel=<kernel-version> -k <kernel-version> <module>"
    fi

    # Error out if $template_kernel = $kernel_version
    if [[ $template_kernel = $kernelver ]]; then
        die 2 $"The templatekernel and the specified kernel version are the same."
    fi

    # Read in the status of template_kernel
    local template_kernel_status=$(do_status '' '' $template_kernel $arch | grep ": installed")

    # If $module is set, grep the status only for that module
    if [[ $module ]]; then
        # Make sure that its installed in the first place
        if ! [[ -d $dkms_tree/$module/ ]]; then
            die 3 $"The module: $module is not located in the DKMS tree."
        fi
        template_kernel_status=$(echo "$template_kernel_status" | grep "^$module,")
    fi

    echo $""
    echo $"Matching modules in kernel: $kernelver ($arch)"
    echo $"to the configuration of kernel: $template_kernel ($arch)"

    # Prepare the kernel just once but only if there is actual work to do
    if [[ ! $template_kernel_status ]]; then
        echo $""
        echo $"There is nothing to be done for this match."
    else
    prepare_kernel "$kernelver" "$arch"

    # Iterate over the kernel_status and match kernel to the template_kernel
    while read template_line; do
        template_module=`echo "$template_line" | awk {'print $1'} | sed 's/,$//'`
        template_version=`echo "$template_line" | awk {'print $2'} | sed 's/,$//'`

        # Print out a match header
        echo $""
        echo $"---- Match Beginning ----"
        echo $"Module:  $template_module"
        echo $"Version: $template_version"
        echo $"-------------------------"

        # Figure out what to do from here
        if show_status "$template_module" "$template_version" "$kernelver" "$arch" 2>/dev/null | grep -q ": installed"; then
            echo $""
            echo $"This module/version combo is already installed.  Nothing to be done."
        elif show_status "$template_module" "$template_version" "$kernelver" "$arch" 2>/dev/null | grep -q ": built"; then
            echo $""
            echo $"This module/version combo is built.  Installing it:"
            module="$template_module"
            module_version="$template_version"
            install_module
        else
        echo $""
        echo $"Building & Installing this module/version:"
        module="$template_module"
        module_version="$template_version"
        build_module
        install_module
        fi
    done < <(echo "$template_kernel_status")

    # Clean up the kernel tree
    if [[ ! ( $(VER $kernelver) < $(VER 2.6.6) ) && -d "$kernel_source_dir" && ! -h "$kernel_source_dir" && -z "$ksourcedir_fromcli" ]]; then
        echo $"Kernel cleanup unnecessary for this kernel.  Skipping..."
    elif [[ ! $no_clean_kernel ]]; then
        cd "$kernel_source_dir"
        [[ $kerneldoth_contents ]] || invoke_command "make mrproper" "cleaning kernel tree (make mrproper)" background
        [[ $config_contents ]] && echo "$config_contents" > .config
        [[ $kerneldoth_contents ]] && echo "$kerneldoth_contents" > /boot/kernel.h
        cd - >/dev/null
    fi
    fi

    # Done
    echo $""
    echo $"DKMS: match completed."
}

make_rpm()
{
    make_common_test "mkrpm"

    # Check that the rpmbuild command is present
    if ! which rpmbuild >/dev/null 2>&1 ; then
        die 1 $"rpmbuild not present." \
            $"Install the rpm-build package."
    fi

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"

    local rpm_basedir="$dkms_tree/$module/$module_version/rpm"

    echo $""
    local sp
    for sp in "$dkms_tree/$module/$module_version/source/$module-dkms-mkrpm.spec" "/etc/dkms/template-dkms-mkrpm.spec"; do
        [[ -e $sp ]] || continue
        SPECFILE="$sp"
        break
    done
    if [[ ! $SPECFILE ]]; then
        die 5 $"Cannot find $sp which is needed by" \
            $"DKMS in order use mkrpm."
    fi

    # Run a dkms mktarball for use in the rpm
    local mktarball_line
    if [[ ! $source_only || $binaries_only ]]; then
        mktarball_line="--binaries-only"
        local i
        echo $""
        for ((i=0; i<${#kernelver[@]}; i++)); do
            if ! [[ -d $dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]} ]]; then
                die 5 $"You do not seem to have $module $module_version built for" \
                    $"${kernelver[$i]} (${arch[$i]}).  All modules must be in" \
                    $"the built state before you can use mkrpm."
            fi
            echo $"Marking ${kernelver[$i]} (${arch[$i]}) for RPM..."
            mktarball_line="-k ${kernelver[$i]} -a ${arch[$i]} $mktarball_line"
        done
    else
        mktarball_line="none"
    fi

    local temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    trap 'rm -rf $temp_dir_name' EXIT HUP TERM
    mkdir -p ${temp_dir_name}/{BUILD,RPMS,SRPMS,SPECS,SOURCES}
    cp ${SPECFILE} ${temp_dir_name}/SPECS/dkms_mkrpm.spec

    # If using legacy mode, install common postinst
    if ((legacy_postinst != 0)); then
        invoke_command "cp '$PREFIX/usr/lib/dkms/common.postinst' '${temp_dir_name}/SOURCES'" "copying legacy postinstall template"
    fi

    # Copy in the source tree
    if [[ ! $binaries_only ]]; then
        invoke_command "cp -Lpr '$dkms_tree/$module/$module_version/source' '${temp_dir_name}/SOURCES/$module-$module_version'" "Copying source tree"
    fi

    if invoke_command "LC_ALL=C rpmbuild --define \"_topdir ${temp_dir_name}\" --define \"version $module_version\" --define \"module_name $module\" --define \"kernel_versions ${kernelver[*]}\" --define \"mktarball_line $mktarball_line\" --define \"__find_provides  /usr/lib/dkms/find-provides\" --define \"_use_internal_dependency_generator 0\" -ba ${temp_dir_name}/SPECS/dkms_mkrpm.spec > ${temp_dir_name}/rpmbuild.log 2>&1" "rpmbuild"; then
        mkdir -p ${rpm_basedir}
        cp -a ${temp_dir_name}/SRPMS/* ${temp_dir_name}/RPMS/*/* ${rpm_basedir}/
        echo $""
        cat ${temp_dir_name}/rpmbuild.log | grep ^Wrote | sed -e "s:${temp_dir_name}/:${rpm_basedir}/:" -e 's:SRPMS/::' -e 's:RPMS/.*/::'
        echo $""
        echo $"DKMS: mkrpm completed."
    else
        cat ${temp_dir_name}/rpmbuild.log >&2
        die 7 $"There was a problem creating your rpm."
    fi
    rm -rf $temp_dir_name
    trap > /dev/null 2>&1
}

preproc_file()
{
    local date_str="$(date -R)"
    echo "modifying $1..."
    sed -e "s/DEBIAN_PACKAGE/$debian_package/g" \
	-e "s/DEBIAN_BUILD_ARCH/$debian_build_arch/g" \
	-e "s/KERNEL_VERSION/$kernelver/g" \
        -e "s/MODULE_NAME/$module/g" \
        -e "s/MODULE_VERSION/$module_version/g" \
        -e "s/DATE_STAMP/$date_str/" "$1" > "$1.dkms-pp"
    mv "$1.dkms-pp" "$1"
}

# Install a package on a debian system.
debian_install()
{
    local getroot  tmpfile i
    local -a packages=("$@")
    for ((i=0; i < ${#packages[@]}; i++)); do
        dpkg-query -s "${packages[$i]}"| egrep -q '^Status:.* installed$' || continue
        unset package[$i]
    done
    # if they are already installed, we are OK.
    [[ ${package[@]} ]] || return
    if ((UID != 0)); then
        # figure out how to get root
        for getroot in su-to-root gksudo kdesu sudo; do
            which $getroot >/dev/null 2>&1 || continue
            case $getroot in
                su-to-root)
                    getroot="$getroot -c"
                    ;;
                gksudo)
                    [[ $DISPLAY ]] || continue
                    getroot="$getroot --description 'DKMS Debian package builder' "
                    ;;
                kdesu)
                    [[ $DISPLAY ]] || continue
                    ;;
            esac
            break
        done
    fi
    if [[ -x /usr/sbin/synaptic && $DISPLAY ]] && tmpfile=$(mktemp_or_die); then
        # Pretty GUI install.
        trap 'rm -f "$tmpfile"' EXIT
        for ((i=0; i=${#packages[@]}; i++)); do
            [[ ${packages[$i]} ]] && echo "install ${packages[$i]}" >>$tmpfile
        done
        $getroot "sh -c '/usr/sbin/synaptic --set-selections --non-interactive --hide-main-window < $tmpfile'"
    else
        $getroot apt-get -y install "${packages[@]}"
    fi
    if (( $? != 0)); then
        die 4 $"Missing ${packages[@]}" \
            $"and unable to install.  Please ask an admin to install for you."
    fi
}

make_debian()
{
    create_type="$1"

    create_temporary_trees
    trap "delete_temporary_trees" EXIT HUP TERM

    make_common_test "mk${create_type}"

    debian_package=${module//_/-}
    if [[ $source_only ]]; then
         debian_build_arch='all'
    else
         debian_build_arch=$(dpkg-architecture -qDEB_BUILD_ARCH)
    fi

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"
    debian_install fakeroot dpkg-dev debhelper

    # Skeleton to load templates from
    local system_mk="$dkms_tree/$module/$module_version/source/$module-dkms-mk${create_type}"
    local local_mk="/etc/dkms/template-dkms-mk${create_type}"
    if [[ -e ${system_mk} ]]; then
        echo $"Using ${system_mk}"
        DEBDIR=${system_mk}
    elif [[ -e ${local_mk} ]]; then
        echo $"Using ${local_mk}"
        DEBDIR=${local_mk}
    else
        die 5 $"Cannot find ${local_mk} which is needed by" \
            $"DKMS in order to use mk${create_type}."
    fi

    # Prepare build directory and copy template
    local temp_dir=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    trap "rm -rf $temp_dir; delete_temporary_trees" EXIT HUP TERM
    local temp_dir_debian="$temp_dir/$debian_package-dkms-$module_version"
    invoke_command "cp -ar '$DEBDIR/' '$temp_dir_debian'" "copying template"
    pushd "$temp_dir_debian" > /dev/null 2>&1
    for file in debian/*; do
        preproc_file "$file"
        chmod 755 "$file"
    done
    popd > /dev/null 2>&1

    # If using legacy mode, install common postinst
    if ((legacy_postinst != 0)); then
        invoke_command "cp '$PREFIX/usr/lib/dkms/common.postinst' '$temp_dir_debian'" "copying legacy postinstall template"
    fi

    # Calculate destination directory
    deb_basedir=$dkms_tree/$module/$module_version/${create_type}
    mkdir -p ${deb_basedir} >/dev/null 2>&1

    # Create deb
    pushd "$temp_dir_debian" > /dev/null 2>&1
    case "$create_type" in
    dsc)
        invoke_command "cp -Lpr '$dkms_tree/$module/$module_version/source' '$temp_dir_debian/$module-$module_version'" "Copying source tree"
        invoke_command "dpkg-buildpackage -S -us -uc 1>/dev/null" "Building source package" || \
            die 7 $"There was a problem creating your ${create_type}."
        echo $""
        echo $"DKMS: mk${create_type} completed."
        invoke_command "mv '$temp_dir/${debian_package}-dkms_${module_version}_source.changes' '$temp_dir/${debian_package}-dkms_${module_version}.dsc' '$temp_dir/${debian_package}-dkms_${module_version}.tar.gz' '$deb_basedir'" "Moving built files to $deb_basedir"
        ;;
    deb)
        invoke_command "cp -Lpr '$dkms_tree/$module/$module_version/source' '$temp_dir_debian/$module-$module_version'" "Copying source tree"
        invoke_command "dpkg-buildpackage -rfakeroot -d -b -us -uc 1>/dev/null" "Building binary package" || \
            die 7 $"There was a problem creating your ${create_type}."
        echo $""
        echo $"DKMS: mk${create_type} completed."
        invoke_command "mv '$temp_dir/${debian_package}-dkms_${module_version}_${debian_build_arch}.deb' '$deb_basedir'" "Moving built files to $deb_basedir"
	;;
    bmdeb)
        local archive_location="$dkms_tree/$module/$module_version/tarball/$module-$module_version.dkms.tar.gz"
        # Force binaries_only
        binaries_only="binaries-only"
        invoke_command "make_tarball" "Gathering binaries"
        if [[ -f $archive_location ]]; then
            invoke_command "cp '$archive_location' '$temp_dir_debian'" "Copying DKMS tarball into DKMS tree"
        else
            die 12 $"Unable to find created tarball."
        fi
        export KVER="$kernelver"
        export KARCH="$arch"
        invoke_command "dpkg-buildpackage -rfakeroot -d -b -us -uc 1>/dev/null" "Building binary package" || \
        die 7 $"There was a problem creating your ${create_type}."
        echo $""
        echo $"DKMS: mk${create_type} completed."
        invoke_command "mv '$temp_dir/${debian_package}-modules-${kernelver}_${module_version}_${debian_build_arch}.deb' '$deb_basedir'" "Moving built files to $deb_basedir"
        ;;
    esac
    popd > /dev/null 2>&1

    if in_temporary_trees; then
        echo "Copying built files to "`pwd`"/.." >&2
        cp "${deb_basedir}/"* ..
    fi

    # Cleanup
    invoke_command "rm $temp_dir -fr" "Cleaning up temporary files"
    delete_temporary_trees || \
        die 7 $"There was a problem cleaning up temporary files."
}

make_common_test()
{
    local create_type=$1
    # Error if $module_version is set but $module is not
    check_module_args $create_type

    # Check that source symlink works
    check_module_exists

    # Make sure that its installed in the first place
    [[ -d $dkms_tree/$module/$module_version ]] ||
        die 3 $"The module/version combo: $module-$module_version" \
        $"is not located in the DKMS tree."
}

make_kmp_srpm()
{
    local temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX)
    trap 'rm -rf $temp_dir_name' EXIT HUP TERM
    mkdir -p $temp_dir_name/{BUILD,RPMS,SRPMS,SPECS,SOURCES}
    pushd "$dkms_tree/$module/$module_version" > /dev/null 2>&1
    # Want to change name of the top-level of the tarball from build to $module-$module_version
    cp -lr build ${module}-${module_version}
    tar cvjf $temp_dir_name/SOURCES/${module}-${module_version}.tar.bz2 ${module}-${module_version} > /dev/null 2>&1
    rm -rf ${module}-${module_version}
    popd > /dev/null 2>&1
    pushd "$temp_dir_name" > /dev/null 2>&1
    invoke_command "rpmbuild --define \"_topdir ${temp_dir_name}\" --target=$arch -bs ${SPECFILE} > ${temp_dir_name}/rpmbuild.log 2>&1" "rpmbuild"
    grep ^Wrote $temp_dir_name/rpmbuild.log > /dev/null 2>&1
    local RC="$?"
    if ((RC == 0)); then
        local kmp_basedir="$dkms_tree/$module/$module_version/rpm"
        mkdir -p $kmp_basedir
        RPMS=$(LANG=C cp -va ${temp_dir_name}/SRPMS/* $kmp_basedir | awk '{print $NF}')
    else
        echo $"rpmbuild error log:"
        cat $temp_dir_name/rpmbuild.log
    fi
    popd > /dev/null 2>&1
    rm -rf $temp_dir_name
    trap > /dev/null 2>&1
    return ${RC}
}

report_build_problem()
{
    # If apport is on the system, files a build problem
    if [ -x /usr/share/apport/apport ] && which python3 >/dev/null; then
        python3 /usr/share/apport/package-hooks/dkms_packages.py -m $module -v $module_version -k ${kernelver[0]}
    fi
    die "$@"
}

# Little helper function for reading args from the commandline.
# it automatically handles -a b and -a=b variants, and returns 1 if
# we need to shift $3.
read_arg() {
    # $1 = arg name
    # $2 = arg value
    # $3 = arg parameter
    local rematch='^[^=]*=(.*)$'
    if [[ $2 =~ $rematch ]]; then
        read "$1" <<< "${BASH_REMATCH[1]}"
    else
        read "$1" <<< "$3"
        # There is no way to shift our callers args, so
        # return 1 to indicate they should do it instead.
        return 1
    fi
}

# A couple of helper functions for parsing out our most common arguments.
# This one allows you to pass -k kernel.version-extra/arch instead of
# -k kernel-version.extra -a arch.
# This makes it harder to pass mismatching numbers of kernel/arch pairs, because
# they are all passed at the same time.
parse_kernelarch(){
    if [[ $1 =~ $mv_re ]]; then
        kernelver[${#kernelver[@]}]="${BASH_REMATCH[1]}"
        arch[${#arch[@]}]="${BASH_REMATCH[2]}"
    else
        kernelver[${#kernelver[@]}]="$1"
    fi
}

# This allows you to pass module and module_version information on the commandline
# in a more convienent form.  Instead of the mostly mandatory and annoying
# -m module -v module_version, you can use either -m module/module_version, 
# or just a raw module/module_version with no -m parameter.  
# This vastly improves readability and discoverability of
# commands on the commandline.
parse_moduleversion(){
    if [[ $1 =~ $mv_re ]]; then
        module="${BASH_REMATCH[1]}"
        module_version="${BASH_REMATCH[2]}"
    else
        module="$1"
    fi
}

check_root() {
    ((UID == 0)) && return
    die 1 $"You must be root to use this command."
}

# Add a passed source tree to the default source location.
# We will check the dkms.conf file to make sure it is valid
# beforehand.
add_source_tree() {
    local from=$(readlink -f $1)
    if ! [[ $from && -f $from/dkms.conf ]]; then
        die 9 $"$1 must contain a dkms.conf file!"
    fi
    check_root
    setup_kernels_arches
    if ! get_pkginfo_from_conf "$from/dkms.conf" ; then
        die 10 $"Malformed dkms.conf file. Cannot load source tree."
    fi
    module="$PACKAGE_NAME"
    module_version="$PACKAGE_VERSION"
    if [[ $force && -d $source_tree/$module-$module_version ]]; then
        echo >&2
        echo $"Forcing install of $module-$module_version"
        rm -rf "$source_tree/$module-$module_version"
    fi

    # We are already installed, just return.
    case $from in
        "$source_tree/$module-$module_version")
            return
            ;;
        "$dkms_tree/$module/$version/source")
            return
            ;;
        "$dkms_tree/$module/$version/build")
            return
            ;;
    esac
    mkdir -p "$source_tree/$module-$module_version"
    cp -fr "$from"/* "$source_tree/$module-$module_version"
}

make_kmp()
{
    make_common_test "mkkmp"

    # Read the conf file
    read_conf_or_die "$kernelver" "$arch"

    echo $""
    if [[ $specfile && -e $dkms_tree/$module/$module_version/source/$specfile ]]; then
        echo $"Using $dkms_tree/$module/$module_version/source/$specfile"
        SPECFILE="$dkms_tree/$module/$module_version/source/$specfile"
    elif [[ -e $dkms_tree/$module/$module_version/source/$module-kmp.spec ]]; then
        echo $"Using $dkms_tree/$module/$module_version/source/$module-kmp.spec"
        SPECFILE="$dkms_tree/$module/$module_version/source/$module-kmp.spec"
    else
        die 5 $"Cannot find a suitable spec file which is needed by" \
            $"DKMS in order use mkkmp.  Please specify --spec=specfile."
    fi

    prepare_build
    make_kmp_srpm
    RC=$?
    clean_build

    if ((RC == 0)); then
        echo $""
        echo $"KMP SRPM location: $RPMS"
        echo $""
        echo $"DKMS: mkkmp completed."
    else
        die 7 $"There was a problem creating your KMP source rpm."
    fi
    # FIXME: hand SRPM to mock or build system to build
}

# This code used to be in dkms_autoinstaller.
# Moving it into the main dkms script gets rid of a fair amount of duplicate
# functionality, and makes it much easier to reinstall DKMS kernel modules
# by hand if dkms_autoinstaller is not used.
autoinstall() {
    local status mv mvka m v k a last_v last_m tenative
    local install_count next_depends
    local -a to_install=()
    local -a next_install=()
    local -a installed_modules=()
    local -A build_depends=()

    # Walk through our list of installed and built modules, and create
    # a list of modules that need to be reinstalled.
    # Assuming all versions of same module to be parsed sequentially.
    while read status mvka; do
        IFS='/' read m v k a <<< "$mvka"
        [[ ! $last_m ]] && last_m="$m"
        # autoinstall previous module's latest version after its deps.
        if [[ $last_m != $m ]]; then
            if [[ $tenative ]]; then
                to_install[${#to_install[@]}]="$tenative"
                build_depends["$last_m"]="${BUILD_DEPENDS[@]}"
            fi
            last_m="$m"
            last_v='0'
            tenative=''
        fi
        # If the module is already installed or weak-installed, skip it.
        if _is_module_installed "$m" "$v" "$kernelver" "$arch"; then
            installed_modules[${#installed_modules[@]}]="$m"
            continue
        fi
        if module_status_weak "$m" "$v" "$kernelver" "$arch" >/dev/null; then
            installed_modules[${#installed_modules[@]}]="$m"
            continue
        fi
        # if the module does not want to be autoinstalled, skip it.
        read_conf_or_die "$k" "$a" "$dkms_tree/$m/$v/source/dkms.conf"
        if [[ ! $AUTOINSTALL ]]; then
            continue
        fi
        # otherwise, only autoinstall the latest version we have hanging around.
        if [[ ($(VER $v) > $(VER $last_v)) ]]; then
            last_v="$v"
            tenative="$m/$v"
        fi
    done < <(module_status)

    # We may have exited the loop with $tenative set.  If it is,
    # it contains something that should be updated.
    if [[ $tenative ]]; then
        to_install[${#to_install[@]}]="$tenative"
        build_depends["$m"]="${BUILD_DEPENDS[@]}"
    fi

    [[ $to_install ]] || return 0

    while true; do
        install_count=0
        next_install=( )

        # Step 1: Remove installed modules from all dependency lists.
        for m in ${!build_depends[@]}; do
            next_depends=
            for d in ${build_depends[$m]}; do
                for i in ${installed_modules[@]}; do
                    [[ "$d" = "$i" ]] && continue 2
                done
                next_depends+="$d "
            done
            build_depends[$m]="${next_depends%% }"
        done

        # Step 2: Install modules that have an empty dependency list.
        for mv in "${to_install[@]}"; do
            IFS=/ read m v <<< "$mv"
            if [[ -z "${build_depends[$m]}" ]]; then
                (module="$m"; module_version="$v"; install_module)
                installed_modules[${#installed_modules[@]}]="$m"
                install_count=$(($install_count +1))
            else
                next_install[${#next_install[@]}]="$mv"
            fi
        done

        wait

        # Step 3: Keep going if at least one module was installed during
        # this iteration.
        [[ "$install_count" -gt 0 ]] || break;

        # Step 4: Remove modules that were installed during Step 2 from
        # the job queue.
        to_install=( "${next_install[@]}" )

    done
    for mv in "${to_install[@]}"; do
        IFS=/ read m v <<< "$mv"
        echo "$m/$v autoinstall failed due to missing dependencies: ${build_depends[$m]}"
    done
}

make_redhat3_driver_disk ()
{
    # Check that the rpmbuild command is present
    if ! which rpmbuild >/dev/null 2>&1 ; then
        echo $"" >&2
        echo $"Error! rpmbuild not present." >&2
        echo $"Install the rpm-build package." >&2
        exit 1
    fi

    local kmodtool=$(rpm -ql redhat-rpm-config | grep kmodtool)
    #Check that the kmodtool is present
    if [ -z "$kmodtool" ]; then
        echo $"Error! kmodtool not present." >&2
        echo $"Install redhat-rpm-config package." >&2
        exit 1
    fi

    # Check that the createrepo command is present
    if ! which createrepo >/dev/null 2>&1 ; then
        echo $"" >&2
        echo $"Error! createrepo not present." >&2
        echo $"Install the createrepo package." >&2
        exit 1
    fi

    echo $""
    if [ -n "$specfile" -a -e "$dkms_tree/$module/$module_version/source/$specfile" ]; then
        echo $"Using $dkms_tree/$module/$module_version/source/$specfile"
        SPECFILE="$dkms_tree/$module/$module_version/source/$specfile"
    elif [ -e "/etc/dkms/template-dkms-redhat-kmod.spec" ]; then
        echo $"Using /etc/dkms/template-dkms-redhat-kmod.spec"
        SPECFILE="/etc/dkms/template-dkms-redhat-kmod.spec"
    else
        echo $"" >&2
        echo $"Cannot find /etc/dkms/template-dkms-redhat-kmod.spec which is needed by" >&2
        echo $"DKMS in order to make Redhat driver disk v3." >&2
        exit 5
    fi
    # Set up temporary build directory for build
    rm -rf "$dkms_tree/$module/$module_version/build"
    cp -a "$dkms_tree/$module/$module_version/source/" "$dkms_tree/$module/$module_version/build"

    cd "$dkms_tree/$module/$module_version/build"

    # Run the pre_build script
    if [ -n "$pre_build" ] && [ -x `echo "$dkms_tree/$module/$module_version/source/$pre_build" | sed 's/ .*//'` ]; then
        echo $""
        echo $"Running the pre_build script:"
        $dkms_tree/$module/$module_version/build/$pre_build
    fi

    # Apply any patches
    local index=0
    while [ $index -lt ${#patch_array[@]} ]; do
        if ! [ -e "$dkms_tree/$module/$module_version/build/patches/${patch_array[$index]}" ]; then
            echo $"" >&2
            echo $"Error!  Patch ${patch_array[$index]} as specified in dkms.conf cannot be" >&2
            echo $"found in $dkms_tree/$module/$module_version/build/patches/." >&2
            exit 5
        fi
        invoke_command "patch -p1 < ./patches/${patch_array[$index]}" "applying patch ${patch_array[$index]}"
        if [ "$?" -ne 0 ]; then
            echo $"" >&2
            echo $"Error! Application of patch ${patch_array[$index]} failed." >&2
            echo $"Check $dkms_tree/$module/$module_version/build/ for more information." >&2
                report_build_problem
            exit 6
        fi
        index=$(($index+1))
    done
    # Create temp dirs and copy files for build
    local temp_dir_name=`mktemp -d $tmp_location/dkms.XXXXXX`
    trap 'rm -rf $temp_dir_name' EXIT HUP TERM
    mkdir -p ${temp_dir_name}/{disk,BUILD,RPMS,SRPMS,SPECS,SOURCES}
    cp ${SPECFILE} ${temp_dir_name}/SPECS/$module.spec
    cp -rax $dkms_tree/$module/$module_version/build/ $temp_dir_name/SOURCES/$module-$module_version/
    # Clean the build directory
    rm -rf "$dkms_tree/$module/$module_version/build"
    cd $temp_dir_name/SOURCES
    invoke_command "tar -jcvf $temp_dir_name/SOURCES/$module-$module_version.tar.bz2 $module-$module_version/" "creating source tarball"
    local i=0
    # Build RPMS
    while [ $i -lt ${#kernelver[@]} ]; do
        invoke_command "LC_ALL=C rpmbuild --define \"_topdir ${temp_dir_name}\" --define \"version $module_version\" --define \"module_name $module\" --define \"kernel_version ${kernelver[$i]}\" -bb --target ${arch[$i]} ${temp_dir_name}/SPECS/$module.spec > ${temp_dir_name}/rpmbuild.log 2>&1" "rpmbuild"
        if [ "$?" -ne 0 ]; then
            echo $"" >&2
            echo $"Error! There was a problem creating your kmod." >&2
            cat ${temp_dir_name}/rpmbuild.log >&2
            exit 7
        fi
        local kabi_whitelist=`rpm -ql kabi-whitelists | grep ${arch[$i]}`
        if [ $kabi_whitelist ]; then
            local module_file=`rpm -qlp ${temp_dir_name}/RPMS/${arch[$i]}/kmod-${module}-${module_version}* | grep ${module}.ko`
            cd ${temp_dir_name}/
            rpm2cpio ${temp_dir_name}/RPMS/${arch[$i]}/kmod-${module}-${module_version}* | cpio -id --quiet .${module_file}
            cd - > /dev/null
            local mod_symbols=( $(modprobe --dump-modversions ${temp_dir_name}/${module_file} | cut -f2) )
            local miss_sym_count=0
            local missing_symbols
            for ((i=0; i < "${#mod_symbols[@]}"; i++))
            do
               if [ -z "`grep -o ${mod_symbols[${i}]} ${kabi_whitelist}`" ]; then
                   missing_symbols[$miss_sym_count]="${mod_symbols[${i}]}"
                   miss_sym_count=$(($miss_sym_count+1))
               fi
            done
            if [ $miss_sym_count -ne 0 ]; then
                echo $"" >&2
                echo $"WARNING: ${module}-${module_version} is using following kernel symbols that are not in the ABI whitelist:"
                echo $"----------------------------------"
                for missing_symbol in ${missing_symbols[*]}
                do
                    echo "$missing_symbol"
                done
                echo $"----------------------------------"
                echo $"" >&2
            else
                echo $"NOTICE: ${module}-${module_version} module seems to use only official Red Hat ABI."
            fi
        else
            echo $"WARNING:${module}-${module_version} module is not checked against Red Hat ABI whitelist."
            echo $"Install 'kabi-whitelists' package and build driver disk again to run the ABI compliance test."
        fi    
        i=$(($i + 1))
    done

    i=0
    while [ $i -lt ${#arch[@]} ]; do
        invoke_command "createrepo --pretty ${temp_dir_name}/RPMS/${arch[$i]}" "creating repo"
        if [ "$?" -ne 0 ]; then
            echo $"" >&2
            echo $"Error! There was a problem creating repository." >&2
            exit 7
        fi
        i=$(($i + 1))
    done

    echo "$module-$module_version driver disk" > "${temp_dir_name}/disk/rhdd3"
    mkdir ${temp_dir_name}/disk/rpms
    cp -rax ${temp_dir_name}/RPMS/* ${temp_dir_name}/disk/rpms/

    local suffix="$(driver_disk_suffix)"
    local image_dir="$dkms_tree/$module/$module_version/driver_disk"
    local image_name="$module-$module_version-dd.$suffix"
    echo $""
    echo $"Creating driver disk on $media media:"
    cd "${temp_dir_name}/disk"
    mkdir -p "$image_dir"
    rm -f "$image_dir/$image_name"
    make_driver_disk_media "$image_dir/$image_name" "${temp_dir_name}/disk"
    rm -rf $temp_dir_name

    echo $""
    echo $"Disk image location: $image_dir/$image_name"
    echo $""
    echo $"DKMS: mkdriverdisk completed."
    
    trap > /dev/null 2>&1
}

#############################
####                     ####
#### Program Starts Here ####
####                     ####
#############################

# Set a standard path
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/lib/dkms"

# Ensure files and directories we create are readable to anyone,
# since we aim to build as a non-root user
umask 022

# Unset environment variables that may interfere with the build
unset CC CXX CFLAGS CXXFLAGS LDFLAGS

# Set important variables
current_kernel=$(uname -r)
current_os=$(uname -s)
dkms_tree="/var/lib/dkms"
source_tree="/usr/src"
install_tree="/lib/modules"
tmp_location=${TMPDIR:-/tmp}
verbose=""
symlink_modules=""
dkms_frameworkconf="/etc/dkms/framework.conf"

# These can come from the environment or the config file
[[ ! ${ADDON_MODULES_DIR} && -e /etc/sysconfig/module-init-tools ]] && . /etc/sysconfig/module-init-tools
addon_modules_dir="${ADDON_MODULES_DIR}"
[[ ! ${addon_modules_dir} ]] && running_distribution="$(distro_version)"
weak_modules="${WEAK_MODULES_BIN}"

# Source in /etc/dkms_framework.conf
[ -e $dkms_frameworkconf ] && . $dkms_frameworkconf 2>/dev/null

# Clear out command line argument variables
module=""
module_version=""
template_kernel=""
distro=""
media=""
release=""
conf=""
kernel_config=""
archive_location=""
kernel_source_dir=""
ksourcedir_fromcli=""
action=""
force=""
force_version_override=""
no_prepare_kernel=""
no_clean_kernel=""
binaries_only=""
source_only=""
all=""
module_suffix=""
module_uncompressed_suffix=""
module_compressed_suffix=""
rpm_safe_upgrade=""
size="1440";
specfile=""
legacy_postinst="1"
declare -a directive_array=() kernelver=() arch=()
weak_modules=''
last_mvka=''
last_mvka_conf=''
try_source_tree=''
die_is_fatal="yes"
[ -x /sbin/weak-modules ] && weak_modules='/sbin/weak-modules'
[ -x /usr/lib/module-init-tools/weak-modules ] && weak_modules='/usr/lib/module-init-tools/weak-modules'
no_depmod=""

action_re='^(remove|(auto|un)?install|match|mk(driverdisk|tarball|rpm|deb|bmdeb|dsc|kmp)|build|add|status|ldtarball)$'

# Parse command line arguments
while (($# > 0)); do
    case $1 in
        --dkmsframework*)
            read_arg dkms_frameworkconf "$1" "$2" || shift
            #immediately load this config
            . $dkms_frameworkconf 2> /dev/null
            ;;
        --module*|-m)
            read_arg _mv "$1" "$2" || shift
            parse_moduleversion "$_mv"
            ;;
        -v)
            read_arg module_version "$1" "$2" || shift
            ;;
        --kernelver*|-k)
            read_arg _ka "$1" "$2" || shift
            parse_kernelarch "$_ka"
            ;;
        --distro*|-d)
            read_arg distro "$1" "$2" || shift
            ;;
        --media*)
            read_arg media "$1" "$2" ||shift
            ;;
        --release*|-r)
            read_arg release "$1" "$2" || shift
            ;;
        --templatekernel*)
            read_arg template_kernel "$1" "$2" || shift
            ;;
        -c)
            read_arg conf "$1" "$2" || shift
            ;;
        --quiet|-q)
            exec >/dev/null 2>&1
            ;;
        --version|-V)
            echo $"dkms:[INSERT_VERSION_HERE]"
            exit 0
            ;;
        --no-prepare-kernel)
            no_prepare_kernel="no-prepare-kernel"
            ;;
        --no-clean-kernel)
            no_clean_kernel="no-clean-kernel"
            ;;
        --no-initrd)
            no_initrd="no-initrd"
            ;;
        --binaries-only)
            binaries_only="binaries-only"
            ;;
        --source-only)
            source_only="source-only"
            ;;
        --force)
            force="true"
            ;;
        --force-version-override)
            force_version_override="true"
            ;;
        --all)
            all="true"
            ;;
        --verbose)
            verbose="true"
            ;;
        --rpm_safe_upgrade)
            rpm_safe_upgrade="true"
            ;;
        --dkmstree*)
            read_arg dkms_tree "$1" "$2" || shift
            ;;
        --sourcetree*)
            read_arg source_tree "$1" "$2" || shift
            ;;
        --installtree*)
            read_arg install_tree "$1" "$2" || shift
            ;;
        --symlink-modules)
            symlink_module="true"
            ;;
        --config*)
            read_arg kernel_config "$1" "$2" || shift
            ;;
        --archive*)
            read_arg archive_location "$1" "$2" || shift
            ;;
        --legacy-postinst*)
            read_arg legacy_postinst "$1" "$2" || shift
            ;;
        --arch*|-a)
            read_arg _aa "$1" "$2" || shift
            arch[${#arch[@]}]="$_aa"
            ;;
        --size*)
            read_arg size "$1" "$2" || shift
            ;;
        --kernelsourcedir*)
            read_arg kernel_source_dir "$1" "$2" || shift
            ksourcedir_fromcli="true"
            ;;
        --directive*)
            read_arg _da "$1" "$2" || shift
            directive_array[${#directive_array[@]}]="$_da"
            ;;
        --spec*)
            read_arg specfile "$1" "$2" || shift
            ;;
        --no-depmod)
            no_depmod="true"
            ;;
        --debug)
            export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): '
            set -x
            ;;
        -j)
            read_arg parallel_jobs "$1" "$2" || shift
            ;;
        -*|--*)
            error $" Unknown option: $1"
            show_usage
            exit 2
            ;;
        *)
            if [[ $1 =~ $action_re ]]; then
                action="$action $1" # Add actions to the action list
            elif [[ -f $1 && $1 = *dkms.conf ]]; then
                try_source_tree="${1%dkms.conf}./" # Flag as a source tree
            elif [[ -d $1 && -f $1/dkms.conf ]]; then
                try_source_tree="$1" # ditto
            elif [[ -f $1 ]]; then
                archive_location="$1" # It is a file, assume it is an archive.
            elif [[ ! $module ]]; then
                parse_moduleversion "$1" # Assume it is a module/version pair.
            else
                warn $"I do not know how to handle $1."
            fi
            ;;
    esac
    shift
done

# Sanity checking

# Error out if binaries-only is set and source-only is set
if [[ $binaries_only && $source_only ]]; then
    die 8 $" You have specified both --binaries-only and --source-only." \
        $"You cannot do this."
fi

# Error if # of arches doesn't match # of kernels
if (( ${#kernelver[@]} != ${#arch[@]} && \
    ${#arch[@]} > 1 )); then
    die 1 $" If more than one arch is specified on the command line, then there" \
        $"must be an equal number of kernel versions also specified (1:1 relationship)."
fi

# Check that kernel version and all aren't both set simultaneously
if [[ $kernelver && $all ]]; then
    die 2 $" You cannot specify a kernel version and also specify" \
        $"--all on the command line."
fi

# Check that arch and all aren't both set simultaneously
if [[ $arch && $all ]]; then
    die 3 $" You cannot specify an arch and also specify" \
        $"--all on the command line."
fi

# If initramfs/initrd rebuild is not requested, skip it with Redhat's weak-modules
if [[ $no_initrd && $weak_modules ]]; then
    weak_modules_no_initrd="--no-initramfs"
fi

# Default to -j<number of CPUs>
parallel_jobs=${parallel_jobs:-$(get_num_cpus)}

# Make sure we're not passing -j0 to make; treat -j0 as just "-j"
[[ "$parallel_jobs" = 0 ]] && parallel_jobs=""

# Run the specified action
if [ -z "$action" ]; then
    show_usage
    die 4 $"No action was specified."
fi

for action_to_run in $action; do
    setup_kernels_arches "$action_to_run"
    case "$action_to_run" in
    remove)
        check_root && remove_module
        ;;
    install)
        check_root && install_modules
        ;;
    autoinstall)
        check_root && autoinstall
        ;;
    match)
        check_root && have_one_kernel && run_match
        ;;
    uninstall)
        check_root && have_one_kernel && uninstall_module
        ;;
    mkdriverdisk)
        check_root && make_driver_disk
        ;;
    build)
        build_modules
        ;;
    add)
        add_module
        ;;
    mktarball)
        make_tarball
        ;;
    mkrpm)
        make_rpm
        ;;
    mkdeb)
        make_debian "deb"
        ;;
    mkbmdeb)
        make_debian "bmdeb"
        ;;
    mkdsc)
        make_debian "dsc"
        ;;
    mkkmp)
        have_one_kernel && make_kmp
        ;;
    status)
        show_status
        ;;
    ldtarball) # Make sure they're root if we're using --force
        if ((UID != 0)) && [[ $force = true ]]; then
            die 1 $"You must be root to use this command with the --force option."
        fi
        load_tarball && add_module
        ;;
    '')
        error $"No action was specified."
        show_usage
        ;;
    *)
        error $"Unknown action specified: $action_to_run"
        show_usage
        ;;
    esac
done
