mirror of https://gitee.com/openkylin/dkms.git
2614 lines
92 KiB
Bash
2614 lines
92 KiB
Bash
#!/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
|
|
# 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_ARCH BUILD_EXCLUSIVE_CONFIG
|
|
BUILD_EXCLUSIVE_KERNEL BUILD_EXCLUSIVE_KERNEL_MIN BUILD_EXCLUSIVE_KERNEL_MAX
|
|
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" | sort -V)
|
|
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"
|
|
|
|
# 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 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]} ]] && \
|
|
((array_size == 1)) && \
|
|
built_module_name[$index]=$PACKAGE_NAME
|
|
[[ ! ${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 distributions
|
|
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
|
|
|
|
# Warn if no modules are specified
|
|
if ((array_size == 0)); then
|
|
echo $"dkms.conf: Warning! Zero modules specified." >&2
|
|
fi
|
|
|
|
# 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_KERNEL_MIN && "$(VER "$1")" < "$(VER "$BUILD_EXCLUSIVE_KERNEL_MIN")" ]] && build_exclude="yes"
|
|
[[ $BUILD_EXCLUSIVE_KERNEL_MAX && "$(VER "$1")" > "$(VER "$BUILD_EXCLUSIVE_KERNEL_MAX")" ]] && build_exclude="yes"
|
|
[[ $BUILD_EXCLUSIVE_ARCH && ! $2 =~ $BUILD_EXCLUSIVE_ARCH ]] && build_exclude="yes"
|
|
if [[ $BUILD_EXCLUSIVE_CONFIG && -e "${kernel_config}" ]]; then
|
|
local kconf
|
|
for kconf in $BUILD_EXCLUSIVE_CONFIG ; do
|
|
case "$kconf" in
|
|
!*) grep -q "^${kconf#!}=[ym]" "${kernel_config}" && build_exclude="yes" ;;
|
|
*) grep -q "^${kconf}=[ym]" "${kernel_config}" || build_exclude="yes" ;;
|
|
esac
|
|
done
|
|
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
|
|
# if the module has neither version nor srcversion/checksum, check the binary files instead
|
|
local verinfo="$(get_module_verinfo "${dkms_module}")"
|
|
if [[ "$(echo "$verinfo" | tr -d '[:space:]')" ]] || diff "${kernels_module}" "${dkms_module}" &>/dev/null; then
|
|
echo $"Module version $(echo "$verinfo" | 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
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
check_module_args() {
|
|
[[ $module && $module_version ]] && return
|
|
die 1 $"Arguments <module> and <module-version> are not specified." \
|
|
$"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: ${3:-$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()
|
|
{
|
|
do_signing=0
|
|
|
|
if [[ ! -f "${kernel_config}" ]]; then
|
|
echo "Kernel config ${kernel_config} not found, modules won't be signed"
|
|
return
|
|
fi
|
|
|
|
if ! grep -q "^CONFIG_MODULE_SIG_HASH=" "${kernel_config}"; then
|
|
echo "The kernel is be built without module signing facility, modules won't be signed"
|
|
return
|
|
fi
|
|
|
|
sign_hash=$(grep "^CONFIG_MODULE_SIG_HASH=" "${kernel_config}" | cut -f2 -d= | sed 's/"//g')
|
|
|
|
# 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
|
|
update-secureboot-policy --enroll-key
|
|
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..."
|
|
if ! command -v openssl >/dev/null; then
|
|
echo "openssl not found, can't generate key and certificate."
|
|
return
|
|
fi
|
|
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
|
|
|
|
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 77 \
|
|
$"The $base_dir/dkms.conf for module $module includes a BUILD_EXCLUSIVE directive which does not match this kernel/arch/config."\
|
|
$"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."
|
|
|
|
# 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_config}" ]]; then
|
|
if grep -q 'CONFIG_CC_IS_CLANG=y' "${kernel_config}"; then
|
|
local cc=clang
|
|
if command -v "$cc" >/dev/null; then
|
|
export CC="$cc"
|
|
export KERNEL_CC="$cc"
|
|
fi
|
|
fi
|
|
|
|
if grep -q 'CONFIG_LD_IS_LLD=y' "${kernel_config}"; then
|
|
local ld=ld.lld
|
|
if command -v "$ld" >/dev/null; then
|
|
export LD="$ld"
|
|
export KERNEL_LD="$ld"
|
|
fi
|
|
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 (( 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_kernel "$kernelver" "$arch"
|
|
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 ((${#built_module_name[@]} > 0)) && [[ ! "${any_module_installed}" ]]; then
|
|
die 6 $"Installation aborted."
|
|
fi
|
|
|
|
# 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"
|
|
|
|
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 | sort -u | 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-<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 $"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 [[ ! -d $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=<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."
|
|
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
|
|
local progress next_depends
|
|
local -a to_install=()
|
|
local -a next_install=()
|
|
local -a known_modules=()
|
|
local -a installed_modules=()
|
|
local -a skipped_modules=()
|
|
local -a failed_modules=()
|
|
local -A build_depends=()
|
|
local -A latest=()
|
|
|
|
# Walk through our list of installed and built modules, and create
|
|
# a list of modules and their latest version.
|
|
while read status mvka; do
|
|
IFS='/' read m v k a <<< "$mvka"
|
|
if [[ -z "${latest["$m"]}" ]]; then
|
|
known_modules[${#known_modules[@]}]="$m"
|
|
latest["$m"]="$v"
|
|
elif [[ ("$(VER "$v")" > "$(VER "${latest["$m"]}")") ]]; then
|
|
latest["$m"]="$v"
|
|
fi
|
|
done < <(module_status)
|
|
|
|
# Walk through our list of known modules, and create
|
|
# a list of modules that need to be reinstalled.
|
|
for m in "${known_modules[@]}"; do
|
|
v="${latest["$m"]}"
|
|
# 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 "$kernelver" "$arch" "$dkms_tree/$m/$v/source/dkms.conf"
|
|
if [[ ! $AUTOINSTALL ]]; then
|
|
continue
|
|
fi
|
|
# Otherwise, autoinstall the latest version we have hanging around.
|
|
to_install[${#to_install[@]}]="$m/$v"
|
|
build_depends["$m"]="${BUILD_DEPENDS[@]}"
|
|
done
|
|
|
|
[[ $to_install ]] || return 0
|
|
|
|
while true; do
|
|
progress=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[@]} ${skipped_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" kernelver="$kernelver" arch="$arch" install_module)
|
|
status=$?
|
|
if [ "$status" = 0 ]; then
|
|
installed_modules[${#installed_modules[@]}]="$m"
|
|
progress=$(($progress +1))
|
|
elif [ "$status" = 77 ]; then
|
|
skipped_modules[${#skipped_modules[@]}]="$m"
|
|
progress=$(($progress +1))
|
|
else
|
|
failed_modules[${#failed_modules[@]}]="$m($status)"
|
|
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.
|
|
[[ "$progress" -gt 0 ]] || break;
|
|
|
|
done
|
|
|
|
if [[ "${#installed_modules[@]}" -gt 0 ]]; then
|
|
echo "dkms autoinstall on $kernelver/$arch succeeded for ${installed_modules[@]}"
|
|
fi
|
|
|
|
if [[ "${#skipped_modules[@]}" -gt 0 ]]; then
|
|
echo "dkms autoinstall on $kernelver/$arch was skipped for ${skipped_modules[@]}"
|
|
fi
|
|
|
|
if [[ "${#failed_modules[@]}" -gt 0 ]]; then
|
|
echo "dkms autoinstall on $kernelver/$arch failed for ${failed_modules[@]}"
|
|
fi
|
|
|
|
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[@]}" -gt 0 || "${#to_install[@]}" -gt 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)
|
|
running_distribution=$(distro_version)
|
|
dkms_tree="/var/lib/dkms"
|
|
source_tree="/usr/src"
|
|
install_tree="/lib/modules"
|
|
tmp_location=${TMPDIR:-/tmp}
|
|
verbose=""
|
|
symlink_modules=""
|
|
|
|
# Check that we can write temporary files
|
|
tmpfile=$(mktemp_or_die)
|
|
echo "Hello, DKMS!" > "$tmpfile"
|
|
if [ "$(cat "$tmpfile")" != "Hello, DKMS!" ]; then
|
|
warn $"dkms will not function properly without some free space in \$TMPDIR ($tmp_location)."
|
|
fi
|
|
rm -f "$tmpfile"
|
|
|
|
# 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}"
|
|
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
|
|
|
|
# The <(cmd) idiom does not work if /proc is not mounted
|
|
read line < <(echo "Hello, DKMS!")
|
|
if [ "$line" != "Hello, DKMS!" ]; then
|
|
warn $"dkms will not function properly if /proc is not mounted."
|
|
fi
|
|
|
|
# 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<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=""
|
|
|
|
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
|