From c2c09589f14ac77fd4e29ca36483dfd80c439074 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:20:55 +0200 Subject: Adding upstream version 3.0.10. Signed-off-by: Daniel Baumann --- dkms.in | 2550 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2550 insertions(+) create mode 100644 dkms.in (limited to 'dkms.in') diff --git a/dkms.in b/dkms.in new file mode 100644 index 0000000..5c9a373 --- /dev/null +++ b/dkms.in @@ -0,0 +1,2550 @@ +#!/bin/bash +# +# Dynamic Kernel Module Support (DKMS) +# Copyright (C) 2003-2008 Dell, Inc. +# by Gary Lerhaupt, Matt Domsch, & Mario Limonciello +# Copyright (C) 2012 by Darik Horn +# +# 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 +# The last group of variables has been deprecated +readonly dkms_conf_variables="CLEAN 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 + PATCH PATCH_MATCH patch_array BUILT_MODULE_NAME + built_module_name BUILT_MODULE_LOCATION built_module_location + DEST_MODULE_NAME dest_module_name + DEST_MODULE_LOCATION dest_module_location + STRIP strip AUTOINSTALL NO_WEAK_MODULES + SIGN_FILE MOK_SIGNING_KEY MOK_CERTIFICATE + + REMAKE_INITRD MODULES_CONF MODULES_CONF_OBSOLETES + MODULES_CONF_ALIAS_TYPE MODULES_CONF_OBSOLETE_ONLY" + +# All of the variables not related to signing we will accept from framework.conf. +readonly dkms_framework_nonsigning_variables="source_tree dkms_tree install_tree tmp_location + verbose symlink_modules autoinstall_all_kernels + modprobe_on_install" +# All of the signing related variables we will accept from framework.conf. +readonly dkms_framework_signing_variables="sign_file mok_signing_key mok_certificate" + +# Some important regular expressions. Requires bash 3 or above. +# Any poor souls still running bash 2 or older really need an upgrade. +readonly mv_re='^([^/]*)/(.*)$' + +# 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 +) + +deprecated() ( + exec >&2 + echo -n $"Deprecated feature: " + 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 |" + echo $" mktarball | ldtarball | status }" + echo $" [options] = [-m module] [-v module-version] [-k kernel-version] [-a arch]" + echo $" [-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]" + echo $" [--binaries-only] [--source-only] [--verbose]" + echo $" [--no-depmod] [--modprobe-on-install] [-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" + 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" + grep -q '\.zst:' /lib/modules/$kernel_test/modules.dep 2>/dev/null && module_compressed_suffix=".zst" + module_suffix="$module_uncompressed_suffix$module_compressed_suffix" +} + +set_kernel_source_dir_and_kconfig() +{ + if [[ -z "${ksourcedir_fromcli}" ]]; then + # $1 = the kernel to base the directory on + kernel_source_dir="$(_get_kernel_dir "$1")" + fi + if [[ -z "${kconfig_fromcli}" ]]; then + kernel_config="${kernel_source_dir}/.config" + fi +} + +check_all_is_banned() +{ + if [[ $all ]]; then + die 5 $"The action $1 does not support the --all parameter." + 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 + check_all_is_banned $1 +} + +# 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) + 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 +} + +# Grab distro information from os-release. +# Falls back to LSB for compliant distros. +distro_version() +{ + if [[ -r /etc/os-release ]]; then + . /etc/os-release + if [[ "$ID" = "ubuntu" ]]; then + # ID_LIKE=debian in ubuntu + echo $ID + elif [[ ${#ID_LIKE[@]} != 0 ]]; then + echo ${ID_LIKE[0]} + else + echo $ID + fi + return + fi + + local DISTRIB_ID + + # 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) + fi + + case ${DISTRIB_ID} in + Fedora) + echo fedora + ;; + RedHatEnterprise*|CentOS|ScientificSL) + echo rhel + ;; + SUSE*) + echo sles + ;; + *) + if [[ ${DISTRIB_ID} ]]; then + echo "${DISTRIB_ID}" + else + echo unknown + 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 + fedora* | rhel* | ovm*) + echo "/extra" && return + ;; + sles* | suse* | opensuse*) + echo "/updates" && return + ;; + debian* | ubuntu*) + echo "/updates/dkms" && return + ;; + arch*) + 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 deliberately 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" + + (( ${#REMAKE_INITRD[@]} )) && deprecated "REMAKE_INITRD ($to_source_file)" + (( ${#MODULES_CONF[@]} )) && deprecated "MODULES_CONF ($to_source_file)" + (( ${#MODULES_CONF_OBSOLETES[@]} )) && deprecated "MODULES_CONF_OBSOLETES ($to_source_file)" + (( ${#MODULES_CONF_ALIAS_TYPE[@]} )) && deprecated "MODULES_CONF_ALIAS_TYPE ($to_source_file)" + (( ${#MODULES_CONF_OBSOLETE_ONLY[@]} )) && deprecated "MODULES_CONF_OBSOLETE_ONLY ($to_source_file)" +} + +# 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 installation 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_and_kconfig "$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" + + # 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]} + 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 + [[ ! $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" + + # Check if clang was used to compile or lld was used to link the kernel. + if [[ -e $kernel_source_dir/vmlinux ]]; then + if readelf -p .comment $kernel_source_dir/vmlinux | grep -q clang; then + make_command="${make_command} CC=clang" + fi + if readelf -p .comment $kernel_source_dir/vmlinux | grep -q LLD; then + make_command="${make_command} LD=ld.lld" + fi + elif [[ -e "${kernel_config}" ]]; then + if grep -q CONFIG_CC_IS_CLANG=y "${kernel_config}"; then + make_command="${make_command} CC=clang" + fi + if grep -q CONFIG_LD_IS_LLD=y "${kernel_config}"; then + make_command="${make_command} LD=ld.lld" + fi + fi + + # 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 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 +} + +# Source specified variables from dkms framework configuration files. +read_framework_conf() { + for i in /etc/dkms/framework.conf /etc/dkms/framework.conf.d/*.conf; do + [[ -e "$i" ]] && safe_source "$i" "$@" + done +} + +# Little helper function for parsing the output of modinfo. +get_module_verinfo(){ + local ver + local srcver + local checksum + local vals= + while read -a vals; do + case "${vals[0]}" in + version:) + ver="${vals[1]}" + checksum="${vals[2]}" + ;; + srcversion:) + srcver="${vals[1]}" + ;; + esac + done < <(modinfo "$1") + + echo -E "${ver}" + # Use obsolete checksum info if srcversion is not available + echo -E "${srcver:-$checksum}" +} + +# Compare two modules' version +# Output: +# "==": They are the same version and the same srcversion +# "=": They are the same version, but not the same srcversion +# ">": 1st one is newer than 2nd one +# "<": 1st one is older than 2nd one +# "?": Cannot determine +# Returns 0 if same version, otherwise 1 +compare_module_version() +{ + readarray -t ver1 <<< "$(get_module_verinfo "$1")" + readarray -t ver2 <<< "$(get_module_verinfo "$2")" + if [[ "${ver1[0]}" = "${ver2[0]}" ]]; then + if [[ "${ver1[1]}" = "${ver2[1]}" ]]; then + echo "==" + else + echo "=" + fi + return 0 + elif [[ ! "$ver1" ]] || [[ ! "$ver2" ]]; then + echo "?" + elif [[ "$(VER "${ver1[0]}")" > "$(VER "${ver2[0]}")" ]]; then + echo ">" + else + echo "<" + fi + return 1 +} + +# Perform some module version sanity checking whenever we are installing +# 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 + 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 "$1" + 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}") + + local cmp_res="$(compare_module_version "${kernels_module}" "${dkms_module}")" + if [[ "${cmp_res}" = ">" ]]; then + if [[ ! "$force" ]]; then + error $"Module version $(get_module_verinfo "${dkms_module}" | head -n 1) for $4${module_suffix}" \ + $"is not newer than what is already found in kernel $1 ($(get_module_verinfo "${kernels_module}" | head -n 1))." \ + $"You may override by specifying --force." + return 1 + fi + elif [[ "${cmp_res}" = "="* ]]; then + if [[ ! "$force" ]]; then + echo $"Module version $(get_module_verinfo "${dkms_module}" | head -n 1) 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 + fi + return 0 +} + +check_module_args() { + [[ $module && $module_version ]] && return + die 1 $"Arguments and are not specified." \ + $"Usage: $1 / or" \ + $" $1 -m / or" \ + $" $1 -m -v " +} + +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 +} + +# 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 + + # 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 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 + + # Check the conf file for sanity + read_conf_or_die "$kernelver" "$arch" "$conf" + + # Create the necessary dkms tree structure + echo $"Creating symlink $dkms_tree/$module/$module_version/source -> $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" +} + +# 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_and_kconfig "$1" + + # Check that kernel-source exists + _check_kernel_dir "$1" || { + die 1 $"Your kernel headers for kernel $1 cannot be found at /lib/modules/$1/build or /lib/modules/$1/source." \ + $"Please install the linux-headers-$1 package or use the --kernelsourcedir option to tell DKMS where it's located." + } +} + +prepare_signing() +{ + # Lazy source in signing related configuration + read_framework_conf $dkms_framework_signing_variables + + if [ ! "${sign_file}" ]; then + case "$running_distribution" in + debian* ) + sign_file="/usr/lib/linux-kbuild-${kernelver%.*}/scripts/sign-file" + ;; + ubuntu* ) + sign_file="$(command -v kmodsign)" + if [[ ! -x "${sign_file}" ]]; then + sign_file="/usr/src/linux-headers-$kernelver/scripts/sign-file" + fi + ;; + esac + if [[ ! -f "${sign_file}" ]]; then + sign_file="/lib/modules/$kernelver/build/scripts/sign-file" + fi + fi + echo "Sign command: $sign_file" + + if [[ ! -f "${sign_file}" ]] || [[ ! -x "${sign_file}" ]]; then + echo "Binary ${sign_file} not found, modules won't be signed" + return + fi + + if [[ -z "${mok_signing_key}" ]]; then + # No custom key specified, use the default key created by update-secureboot-policy for Ubuntu + # Debian's update-secureboot-policy has no --new-key option + case "$running_distribution" in + ubuntu* ) + mok_signing_key="/var/lib/shim-signed/mok/MOK.priv" + mok_certificate="/var/lib/shim-signed/mok/MOK.der" + + if [[ ! -f "${mok_signing_key}" ]] || [[ ! -f "${mok_certificate}" ]]; then + if [[ ! -x "$(command -v update-secureboot-policy)" ]]; then + echo "Binary update-secureboot-policy not found, modules won't be signed" + return + fi + # update-secureboot-policy won't create new key if $mok_certificate exists + if [[ -f "${mok_certificate}" ]]; then + rm -f "${mok_certificate}" + fi + echo "Certificate or key are missing, generating them using update-secureboot-policy..." + SHIM_NOTRIGGER=y update-secureboot-policy --new-key &>/dev/null + fi + + ;; + esac + fi + + if [ ! "${mok_signing_key}" ]; then + mok_signing_key="/var/lib/dkms/mok.key" + fi + echo "Signing key: $mok_signing_key" + + if [ ! "${mok_certificate}" ]; then + mok_certificate="/var/lib/dkms/mok.pub" + fi + echo "Public certificate (MOK): $mok_certificate" + + # scripts/sign-file.c in kernel source also supports using "pkcs11:..." as private key + if [[ "$mok_signing_key" != "pkcs11:"* ]] && ( [ ! -f "$mok_signing_key" ] || [ ! -f "$mok_certificate" ] ); then + echo "Certificate or key are missing, generating self signed certificate for MOK..." + openssl req -new -x509 -nodes -days 36500 -subj "/CN=DKMS module signing key" \ + -newkey rsa:2048 -keyout "$mok_signing_key" \ + -outform DER -out "$mok_certificate" > /dev/null 2>&1 + if [ ! -f "${mok_signing_key}" ]; then + echo "Key file ${mok_signing_key} not found and can't be generated, modules won't be signed" + return + fi + fi + + if [ ! -f "${mok_certificate}" ]; then + echo "Certificate file ${mok_certificate} not found and can't be generated, modules won't be signed" + return + fi + + if [[ -f "${kernel_config}" ]]; then + sign_hash="$(grep "^CONFIG_MODULE_SIG_HASH=" "${kernel_config}" | sed 's/CONFIG_MODULE_SIG_HASH=//;s/"//g')" + # The kernel may be built without module signing facility. CONFIG_MODULE_SIG_HASH is unset in kconfig + sign_hash="${sign_hash:-sha512}" + else + sign_hash="sha512" + fi + + do_signing=1 +} + +# 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 + + local -r base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch" + local -r build_dir="$dkms_tree/$module/$module_version/build" + local -r source_dir="$dkms_tree/$module/$module_version/source" + + # 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 $base_dir/dkms.conf for module $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 $source_dir | wc -l | awk {'print $1'}) < 2)) && die 8 \ + $"The directory $source_dir 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 "$build_dir" + cp -a "$source_dir/" "$build_dir" + + cd "$build_dir" + + # Apply any patches + for p in "${patch_array[@]}"; do + [[ ! -e $build_dir/patches/$p ]] && \ + report_build_problem 5 \ + $" Patch $p as specified in dkms.conf cannot be" \ + $"found in $build_dir/patches/." + invoke_command "patch -p1 < ./patches/$p" "applying patch $p" || \ + report_build_problem 6 $"Application of patch $p failed." \ + $"Check $build_dir for more information." + done + + if [[ -e $kernel_source_dir/.config ]]; then + cc=$(sed -n 's|^CONFIG_CC_VERSION_TEXT="\([^ ]*\) .*"|\1|p' $kernel_source_dir/.config) + if command -v "$cc" >/dev/null; then + export CC="$cc" + fi + fi + + # 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. +actual_build() +{ + local -r base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch" + local -r build_dir="$dkms_tree/$module/$module_version/build" + local -r build_log="$build_dir/make.log" + + echo $"" + echo $"Building module:" + + if [ -f "$kernel_source_dir/.kernelvariables" ]; then + export CC=$(echo -e "show-%:\n\t@echo \$(\$*)\ninclude $kernel_source_dir/.kernelvariables" | make -f - show-CC) + else + unset CC + fi + + invoke_command "$clean" "Cleaning build area" background + echo $"DKMS make.log for $module-$module_version for kernel $kernelver ($arch)" >> "$build_log" + date >> "$build_log" + local the_make_command="${make_command/#make/make -j$parallel_jobs KERNELRELEASE=$kernelver}" + + invoke_command "{ $the_make_command; } >> $build_log 2>&1" "$the_make_command" background || \ + report_build_problem 10 $"Bad return status for module build on kernel: $kernelver ($arch)" \ + $"Consult $build_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)" \ + $"Make sure the name of the generated module is correct and at the root of the" \ + $"build directory, or consult make.log in the build directory" \ + $"$build_dir 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 "$build_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 + local the_module="$build_dir/${built_module_location[$count]}${built_module_name[$count]}" + local built_module="$the_module$module_uncompressed_suffix" + local compressed_module="$the_module$module_suffix" + + [[ ${strip[$count]} != no ]] && strip -g "$built_module" + + if [ -n "${do_signing}" ]; then + echo "Signing module $built_module" + "$sign_file" "$sign_hash" "$mok_signing_key" "$mok_certificate" "$built_module" + fi + + if [ "$module_compressed_suffix" = ".gz" ]; then + gzip -9f "$built_module" || compressed_module="" + elif [ "$module_compressed_suffix" = ".xz" ]; then + xz -f "$built_module" || compressed_module="" + elif [ "$module_compressed_suffix" = ".zst" ]; then + zstd -q -f -T0 -20 --ultra "$built_module" || compressed_module="" + fi + if [ -n "$compressed_module" ]; then + cp -f "$compressed_module" "$base_dir/module/${dest_module_name[$count]}$module_suffix" >/dev/null + else + cp -f "$built_module" "$base_dir/module/${dest_module_name[$count]}$module_uncompressed_suffix" >/dev/null + fi + 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 + + # Clean the build directory + rm -rf "$dkms_tree/$module/$module_version/build" +} + +do_build() +{ + set_kernel_source_dir_and_kconfig "$kernelver" + prepare_signing + prepare_build + actual_build + clean_build +} + +# 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. +do_install() +{ + # If the module has not been built, try to build it first. + is_module_built "$module" "$module_version" "$kernelver" "$arch" || do_build + local base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch" + + # 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 any_module_installed + 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") + 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##*/}" + any_module_installed=1 + + done + + if [[ ! "${any_module_installed}" ]]; then + die 6 $"Installation aborted." + fi + + # Create the kernel- 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" + + invoke_command "do_depmod $kernelver" "depmod" background || { + do_uninstall "$kernelver" "$arch" + die 6 $"Problems with depmod detected. Automatically uninstalling this module." \ + $"Install Failed (depmod problems). Module rolled back to built state." + exit 6 + } + + if [[ $modprobe_on_install ]]; then + # 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 + fi + + # 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" && { + if [[ "$force" = "true" ]]; then + do_unbuild "$3" "$4" + else + echo $"Module $1/$2 already built for kernel $3 ($4), skip." \ + $"You may override by specifying --force." + return 0 + fi + } + module="$1" module_version="$2" kernelver="$3" arch="$4" do_build +) + +maybe_install_module() ( + is_module_installed "$1" "$2" "$3" "$4" && { + if [[ "$force" = "true" ]]; then + do_uninstall "$3" "$4" + else + echo $"Module $1/$2 already installed on kernel $3 ($4), skip." \ + $"You may override by specifying --force." + return 0 + fi + } + module="$1" module_version="$2" kernelver="$3" arch="$4" do_install +) + +build_module() { + local i=0 + for ((i=0; i < ${#kernelver[@]}; i++)); do + maybe_build_module "$module" "$module_version" "${kernelver[$i]}" "${arch[$i]}" + done +} + +install_module() { + local i=0 + for ((i=0; i < ${#kernelver[@]}; i++)); do + maybe_install_module "$module" "$module_version" "${kernelver[$i]}" "${arch[$i]}" + done +} + +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}" ]] && compare_module_version "${dkms_owned}" "${installed}" &>/dev/null; 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 $"Module $module-$module_version for kernel $1 ($2)." + + set_module_suffix "$1" + + # If kernel- 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 $"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" + if [[ "${real_dest_module_location}" ]]; then + 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 + (if cd "$install_tree/$1"; then rpm -qf "${dir_to_remove}" >/dev/null 2>&1 || rmdir -p --ignore-fail-on-non-empty "${dir_to_remove}"; fi || true) + else + echo $" - Module was not found within $install_tree/$1/" + fi + 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." + fi + done + rm -f "$dkms_tree/$module/kernel-$1-$2" + else + echo $"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 + + # 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" +} + +module_is_added_or_die() +{ + is_module_added "$module" "$module_version" || die 3 \ + $"The module/version combo: $module-$module_version is not located in the DKMS tree." +} + +maybe_unbuild_module() +{ + is_module_built "$module" "$module_version" "$1" "$2" || { + echo $"Module $module $module_version is not built for kernel $1 ($2)."\ + $"Skipping..." + return 0 + } + + do_unbuild "$1" "$2" +} + +maybe_uninstall_module() +{ + is_module_installed "$module" "$module_version" "$1" "$2" || { + echo $"Module $module $module_version is not installed for kernel $1 ($2)."\ + $"Skipping..." + return 0 + } + do_uninstall "$1" "$2" +} + +uninstall_module() +{ + local i + for ((i=0; i < ${#kernelver[@]}; i++)); do + maybe_uninstall_module "${kernelver[$i]}" "${arch[$i]}" + done +} + +do_unbuild() +{ + # Delete or "unbuild" the $kernel_version/$arch_used part of the tree + rm -rf "$dkms_tree/$module/$module_version/$1/$2" + [[ $(find $dkms_tree/$module/$module_version/$1/* -maxdepth 0 -type d 2>/dev/null) ]] || \ + rm -rf "$dkms_tree/$module/$module_version/$1" +} + +# Remove the build module, w/o removing/unregistering it. +# This uninstalls any installed modules along the way +unbuild_module() +{ + local i + for ((i=0; i < ${#kernelver[@]}; i++)); do + maybe_uninstall_module "${kernelver[$i]}" "${arch[$i]}" + maybe_unbuild_module "${kernelver[$i]}" "${arch[$i]}" + done +} + +# Unregister a DKMS module. This uninstalls any installed modules along the way. +remove_module() +{ + # 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 $"Remove cancelled because --rpm_safe_upgrade scenario detected." + done + fi + + local i + for ((i=0; i < ${#kernelver[@]}; i++)); do + maybe_uninstall_module "${kernelver[$i]}" "${arch[$i]}" + maybe_unbuild_module "${kernelver[$i]}" "${arch[$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 | grep -Eqv "(build|tarball|driver_disk|rpm|deb|source)$"; then + echo $"Deleting module $module-$module_version completely from the DKMS tree." + rm -rf "$dkms_tree/$module/$module_version" + 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 + 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" 2>/dev/null + [[ -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 inconvenient. +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 + # separate deprecation warnings from status output + local tmpfile=$(mktemp_or_die) + (module_status "$@") >"$tmpfile" + 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 < "$tmpfile" + rm "$tmpfile" +} + +# 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 +} + +make_tarball() +{ + # Read the conf file + read_conf_or_die "$kernelver" "$arch" + + local -r temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX) + trap "rm -rf $temp_dir_name" EXIT + 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 + local -r intree_module_dir="$dkms_tree/$module/$module_version/${kernelver[$i]}/${arch[$i]}" + local -r temp_module_dir="$temp_dir_name/dkms_main_tree/${kernelver[$i]}" + + if ! [[ -d "$intree_module_dir" ]]; then + 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_module_dir" + cp -rf "$intree_module_dir" "$temp_module_dir" + done + fi + + local -r source_dir="$dkms_tree/$module/$module_version/source" + + # Copy the source_tree or make special binaries-only structure + if [[ $binaries_only ]]; then + local -r binary_only_dir="$temp_dir_name/dkms_binaries_only" + + echo $"" + echo $"Creating tarball structure to specifically accomodate binaries." + + mkdir "$binary_only_dir" + echo "$module" > "$binary_only_dir/PACKAGE_NAME" + echo "$module_version" > "$binary_only_dir/PACKAGE_VERSION" + [[ ! $conf ]] && conf="$source_dir/dkms.conf" + cp -f $conf "$binary_only_dir/" 2>/dev/null + else + echo $"" + echo $"Marking $source_dir for archiving..." + mkdir -p $temp_dir_name/dkms_source_tree + cp -rf $source_dir/* $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/" + + if [[ $archive_location ]]; then + tarball_name="${archive_location##*/}" + if [[ ${archive_location%/*} != $archive_location ]]; then + tarball_dest="${archive_location%/*}" + fi + fi + + echo $"" + echo $"Tarball location: $tarball_dest/$tarball_name" + + if [[ ! -d $tarball_dest ]]; then + if ! mkdir -p "$tarball_dest" 2>/dev/null; then + die 9 $"Missing write permissions for $tarball_dest." + fi + fi + + [[ -w $tarball_dest ]] || die 9 $"Missing write permissions for $tarball_dest." + + if ! tar -C $temp_dir_name -caf $tarball_dest/$tarball_name . 2>/dev/null; then + die 6 $"Failed to make tarball." + fi +} + +# 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 + + # Untar it into $tmp_location + local -r temp_dir_name=$(mktemp_or_die -d $tmp_location/dkms.XXXXXX) + trap "rm -rf $temp_dir_name" EXIT + tar -xaf $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 + 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 + + # 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 + # Success! + break + done + + 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 + local -r source_dir="$dkms_tree/$module/$module_version/source" + + echo $"Creating $source_dir" + mkdir -p "$source_dir" + echo $"Copying dkms.conf to $source_dir ..." + cp -rf "$temp_dir_name/dkms_binaries_only/dkms.conf" "$source_dir" + fi + ;; + esac + + # 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. + + # Load precompiled modules. + for directory in "$temp_dir_name/dkms_main_tree"/*/*; do + [[ -d $directory ]] || continue + + local -r kernel_arch_to_load=${directory/*dkms_main_tree\/} + local -r 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 + + [[ $loc != dkms_binaries_only ]] || [[ -d $source_tree/$module-$module_version ]] +} + +run_match() +{ + set_kernel_source_dir_and_kconfig "$kernelver" + + # Error if $template_kernel is unset + if [[ ! $template_kernel ]]; then + die 1 $"Invalid number of parameters passed." \ + $"Usage: match --templatekernel= -k " \ + $" or: match --templatekernel= -k " + 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." + return 0 + fi + + 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 $"Module: $template_module" + echo $"Version: $template_version" + + maybe_build_module "$template_module" "$template_version" "$kernelver" "$arch" + maybe_install_module "$template_module" "$template_version" "$kernelver" "$arch" + done < <(echo "$template_kernel_status") +} + +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 convenient 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() { + [[ $(id -u) = 0 ]] && return + die 1 $"You must be root to use this command." +} + +check_rw_dkms_tree() { + [[ -w "$dkms_tree" ]] && return + die 1 $"No write access to DKMS tree at ${dkms_tree}" +} + +# 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" +} + +# 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 failed_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 + if (module="$m" module_version="$v" kernelver="$kernelver" arch="$arch" install_module); then + installed_modules[${#installed_modules[@]}]="$m" + install_count=$(($install_count +1)) + else + failed_modules=[${#failed_modules[@]}]="$m" + fi + else + next_install[${#next_install[@]}]="$mv" + fi + done + + wait + + # Step 3: Remove modules that install was attempted for + # during Step 2 from the job queue. + to_install=( "${next_install[@]}" ) + + # Step 4: Keep going if at least one module was installed during + # this iteration. + [[ "$install_count" -gt 0 ]] || break; + + 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 + + if [[ "${#failed_modules[@]}" > 0 || "${#to_install[@]}" > 0 ]]; then + die 11 $"One or more modules failed to install during autoinstall." \ + $"Refer to previous errors for more information." + fi +} + +############################# +#### #### +#### Program Starts Here #### +#### #### +############################# + +# Set a standard path +PATH="$PATH:/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="" + +# 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 configuration not related to signing +read_framework_conf $dkms_framework_nonsigning_variables + + +# Clear out command line argument variables +module="" +module_version="" +template_kernel="" +conf="" +kernel_config="" +kconfig_fromcli="" +archive_location="" +kernel_source_dir="" +ksourcedir_fromcli="" +action="" +force="" +force_version_override="" +binaries_only="" +source_only="" +all="" +module_suffix="" +module_uncompressed_suffix="" +module_compressed_suffix="" +rpm_safe_upgrade="" +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|mktarball|(un)?build|add|status|ldtarball)$' + +# Parse command line arguments +while (($# > 0)); do + case $1 in + --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" + ;; + --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 $"#RELEASE_STRING#" + exit 0 + ;; + --no-initrd) + # This is an old option, consume and warn + deprecated $"--no-initrd" + ;; + --no-clean-kernel) + # This is an old option, consume and warn + deprecated $"--no-clean-kernel" + ;; + --no-prepare-kernel) + # This is an old option, consume and warn + deprecated $"--no-prepare-kernel" + ;; + --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 + kconfig_fromcli="true" + ;; + --archive*) + read_arg archive_location "$1" "$2" || shift + ;; + --arch*|-a) + read_arg _aa "$1" "$2" || shift + arch[${#arch[@]}]="$_aa" + ;; + --kernelsourcedir*) + read_arg kernel_source_dir "$1" "$2" || shift + ksourcedir_fromcli="true" + ;; + --directive*) + read_arg _da "$1" "$2" || shift + directive_array[${#directive_array[@]}]="$_da" + ;; + --no-depmod) + no_depmod="true" + ;; + --modprobe-on-install) + modprobe_on_install="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 ]] && die 4 $"Cannot specify more than one 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 + +# Since initramfs/initrd rebuild is not requested, skip it with Redhat's weak-modules +if [[ $weak_modules ]]; then + weak_modules_no_initrd="--no-initramfs" +fi + +# Default to -j +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="" + +setup_kernels_arches "$action" +case "$action" in +remove | unbuild | uninstall) + check_module_args $action + module_is_added_or_die + [[ $action = uninstall ]] && check_root || check_rw_dkms_tree + ${action}_module + ;; +add | build | install) + check_all_is_banned $action # TODO: fix/enable --all + [[ $action = install ]] && check_root || check_rw_dkms_tree + ${action}_module + ;; +autoinstall) + check_root && autoinstall + ;; +match) + check_root && have_one_kernel "match" && run_match + ;; +mktarball) + check_module_args mktarball + module_is_added_or_die + make_tarball + ;; +status) + show_status + ;; +ldtarball) # Make sure they're root if we're using --force + if [[ $(id -u) != 0 ]] && [[ $force = true ]]; then + die 1 $"You must be root to use this command with the --force option." + fi + load_tarball && add_module + ;; +*) + error $"Unknown action specified: \"$action\"" + show_usage + ;; +esac -- cgit v1.2.3