327 lines
9.1 KiB
Bash
Executable File
327 lines
9.1 KiB
Bash
Executable File
#!/bin/bash -efu
|
|
#
|
|
# Install debuginfo RPMs for a kernel
|
|
#
|
|
# Copyright (C) 2023,2024 Vitaly Chikunov <vt@altlinux.org>
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
set +o posix
|
|
shopt -s extglob
|
|
export LC_ALL=C
|
|
|
|
cr=$'\n'
|
|
PROG=${0##*/}
|
|
message() {
|
|
echo >&2 -e "$PROG: $*"
|
|
}
|
|
|
|
fatal() {
|
|
message "$@"
|
|
exit 1
|
|
}
|
|
|
|
log() {
|
|
[ "$verbose" -lt "$1" ] && return
|
|
shift
|
|
echo "$*"
|
|
}
|
|
|
|
show_help()
|
|
{
|
|
cat <<EOF
|
|
Usage: $PROG [OPTIONS...] [-f|-r] REFERENCE
|
|
Find and install debuginfo package corresponding to already installed kernel from
|
|
the ALT Packages Archive (requires Internet access for direct downloads)
|
|
|
|
Options:
|
|
-F, --file REFERENCE is a filename (such as /boot/vmlinuz)
|
|
-f, -y, --force Force installation (assume 'yes' to all prompts)
|
|
--headers-only Only install kernel headers
|
|
-H, --headers Add kernel headers to install plan
|
|
-h, --help This help
|
|
-d, --download-only Tell apt to download packages but don't install them
|
|
-n, --dry-run Tell apt to simulate action but not to change the system
|
|
Note that apt-get may still download the target packages
|
|
-q, --quiet Do not show (experimental & Internet access) warning"
|
|
--reinstall Try to reihnstall packages
|
|
-r, --release REFERENCE is a kernel release (such as 6.6.1-alt1)
|
|
-v, --verbose Increase verbosity
|
|
|
|
Note: If there is a dependency between debuginfo packages, it won't be automatically resolved.
|
|
|
|
EOF
|
|
exit "$@"
|
|
}
|
|
|
|
declare -i verbose=0
|
|
dryrun=
|
|
force=
|
|
headers=
|
|
headers_modules=y
|
|
isfile=
|
|
isrelease=
|
|
kernel=y
|
|
quiet=
|
|
reinstall=
|
|
|
|
TEMP=$(getopt -n "$PROG" -o f,d,y,F,r,v,n,H,q,h -l file,reinstall,release,verbose,dry-run,force,download-only,headers,headers-only,no-headers-modules,no-kernel,quiet,help -- "$@") || show_help 1
|
|
eval set -- "$TEMP"
|
|
|
|
while :; do
|
|
case "$1" in
|
|
-n | --dry-run) dryrun+=" --dry-run" ;;
|
|
-d | --download-only) dryrun+=" --download-only" ;;
|
|
-F | --file) isfile=y ;;
|
|
-f | --force) force=y ;;
|
|
--headers-only) headers=y; kernel= ;;
|
|
-H | --headers) headers=y ;;
|
|
-h | --help) show_help ;;
|
|
--no-headers-modules) headers_modules= ;;
|
|
--no-kernel) kernel= ;;
|
|
-q | --quiet) quiet=y ;;
|
|
--reinstall) reinstall=--reinstall ;;
|
|
-r | --release) isrelease=y ;;
|
|
--) shift; break ;;
|
|
-v | --verbose) verbose+=1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ "$#" -lt 1 ]]; then
|
|
if [[ -n "$isfile" ]] || [[ -n "$isrelease" ]]; then
|
|
fatal "Argument is required"
|
|
fi
|
|
elif [[ "$#" -gt 1 ]]; then
|
|
fatal "Too many arguments"
|
|
fi
|
|
ref=${1-}
|
|
|
|
if [ -t 1 ] && [ ! -v NO_COLOR ]; then
|
|
# No need to determine FGBG colors if we don't use bright colors.
|
|
GREEN=$'\e[32m' YELLOW=$'\e[33m' MAGENTA=$'\e[35m' BRIGHT=$'\e[1m' NORM=$'\e[0m'
|
|
else
|
|
GREEN='' YELLOW='' MAGENTA='' BRIGHT='' NORM=''
|
|
fi
|
|
|
|
if [ -n "$verbose" ]; then
|
|
V() { echo >&2 "+ $*"; "$@"; }
|
|
else
|
|
V() { "$@"; }
|
|
fi
|
|
|
|
confirmator() {
|
|
if [ -n "$force" ]; then
|
|
echo "yes (forced)"
|
|
return
|
|
fi
|
|
while true; do
|
|
read -r || { echo "Aborting"; exit 1; }
|
|
shopt -s nocasematch
|
|
case "$REPLY" in
|
|
n|no|0|q) exit 0 ;;
|
|
y|ye|yes|'') break ;;
|
|
*) ;;
|
|
esac
|
|
echo -n "[Y/n] "
|
|
shopt -u nocasematch
|
|
done
|
|
}
|
|
|
|
if [[ -z "$quiet" ]]; then
|
|
echo >&2 "***************************************************************************"
|
|
echo >&2 "* This is an experimental, developers-only tool. There is no guarantees *"
|
|
echo >&2 "* that installing an old package from the Archive will work. *"
|
|
echo >&2 "***************************************************************************"
|
|
fi
|
|
|
|
find_kernel_package() {
|
|
# shellcheck disable=SC2207
|
|
pkg=( $(rpmquery "$@") )
|
|
if [[ ! -v pkg ]]; then
|
|
fatal "Kernel with requested release ($release) not installed"
|
|
elif [[ "${#pkg[@]}" -gt 1 ]]; then
|
|
warning "$PROG: Too much packages found for requested release ($release):"
|
|
printf "%s\n" "${pkg[@]}" | cat -n >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
file=
|
|
release=
|
|
if [[ -n "$isfile" ]]; then
|
|
file=$ref
|
|
elif [[ -n "$isrelease" ]]; then
|
|
release=$ref
|
|
elif [[ -e "$ref" ]]; then
|
|
file=$ref
|
|
elif [[ "$ref" =~ / ]]; then
|
|
file=$ref
|
|
else
|
|
release=$ref
|
|
fi
|
|
unset ref isfile isrelease
|
|
|
|
if [[ -n "$file" ]]; then
|
|
if [[ ! -e "$file" ]]; then
|
|
fatal "Requested file ($file) not found"
|
|
elif [[ -L "$file" ]]; then
|
|
echo "Requested file ($file) is a symlink (following)"
|
|
file=$(realpath "$file")
|
|
fi
|
|
echo "User requested kernel file $file"
|
|
elif [[ -z "$release" ]]; then
|
|
uname_r=$(uname -r)
|
|
echo "Booted kernel release: $uname_r"
|
|
if [[ -e "/boot/vmlinuz-$uname_r" ]]; then
|
|
file="/boot/vmlinuz-$uname_r"
|
|
else
|
|
fatal "'vmlinuz' for the booted kernel not found"
|
|
fi
|
|
elif [[ "$release" =~ ^([0-9.]+)-([[:alnum:]-]+)-(alt.*)$ ]]; then
|
|
# 6.5.9-un-def-alt1
|
|
find_kernel_package -a \
|
|
V="${BASH_REMATCH[1]}" \
|
|
R="${BASH_REMATCH[3]}" \
|
|
N="kernel-image-${BASH_REMATCH[2]}"
|
|
elif [[ "$release" =~ ^([0-9.]+)-(alt.*)$ ]]; then
|
|
# 6.5.9-alt1
|
|
find_kernel_package -a \
|
|
V="${BASH_REMATCH[1]}" \
|
|
R="${BASH_REMATCH[2]}" \
|
|
N="kernel-image-*"
|
|
elif [[ "$release" =~ ^kernel-(image|modules)- ]] && rpmquery "$release" &>/dev/null; then
|
|
# kernel-image-un-def-1:6.5.9-alt1.x86_64
|
|
find_kernel_package -q "$release"
|
|
elif [[ "$release" =~ ^(kernel-image-[[:alnum:]-]+)-([0-9]+:)?([0-9.]+)-(alt.*)$ ]]; then
|
|
# kernel-image-un-def-6.5.9-alt1
|
|
find_kernel_package -a \
|
|
N="${BASH_REMATCH[1]}" \
|
|
E="${BASH_REMATCH[2]}" \
|
|
V="${BASH_REMATCH[3]}" \
|
|
R="${BASH_REMATCH[4]}"
|
|
else
|
|
fatal "Release '$release' in unknown format"
|
|
fi
|
|
unset release
|
|
|
|
if [[ -n "$file" ]]; then
|
|
if ! pkg=$(rpmquery -f "$file" 2>/dev/null); then
|
|
fatal "Requested file ($file) is not form a package"
|
|
elif [[ ! "$pkg" =~ ^kernel-(image|modules)- ]]; then
|
|
fatal "Requested file ($file) is not from a kernel package (but from $pkg)"
|
|
fi
|
|
fi
|
|
unset file
|
|
|
|
if [[ "$pkg" =~ ^kernel-([^-]*)- ]]; then
|
|
echo "Kernel ${BASH_REMATCH[1]} package: $BRIGHT$pkg$NORM"
|
|
fi
|
|
fquery='arch=%{ARCH:shescape}
|
|
disttag=%{DISTTAG:shescape}
|
|
name=%{N:shescape}
|
|
ver=%{V:shescape}
|
|
rel=%{R:shescape}'
|
|
eval "$(rpmquery --qf "$fquery" -- "$pkg")"
|
|
# (none) will become 1-element array.
|
|
disttag=${disttag#none}
|
|
[[ -n "$disttag" ]] || fatal "Package disttag not found"
|
|
[[ "$disttag" =~ ^[[:alnum:]]+\+[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]] \
|
|
|| fatal "Package disttag in unknown format: $disttag"
|
|
repo=${disttag%+*}
|
|
taskit=${disttag#*+}
|
|
task=${taskit%%.*}
|
|
subtask=${taskit#"$task."}
|
|
subtask=${subtask%%.*}
|
|
# shellcheck disable=SC2015
|
|
[[ -n "$repo" ]] && [[ -n "$task" ]] && [[ -n "$subtask" ]] || fatal "Package disttag parse error"
|
|
# shellcheck disable=SC2154
|
|
log 1 "Package build info: repo=$repo task=$task subtask=$subtask arch=$arch"
|
|
# shellcheck disable=SC2154
|
|
flavour=${name#kernel-image-}
|
|
|
|
declare -a plan
|
|
declare -i requested=0
|
|
to_plan() {
|
|
local rpm=$1
|
|
requested+=1
|
|
if rpmquery -- "$rpm:$disttag" &>/dev/null; then
|
|
echo -n "- Package $rpm is already installed"
|
|
if [[ -z "$reinstall" ]]; then
|
|
echo
|
|
return
|
|
else
|
|
echo " (reinstall)"
|
|
fi
|
|
elif rpmquery -- "$rpm" &>/dev/null; then
|
|
echo "Package $rpm is installed but have different build (update)"
|
|
fi
|
|
plan+=( "$rpm" )
|
|
}
|
|
|
|
# Generate URLs.
|
|
if rpm -q "apt-https" &>/dev/null; then
|
|
baseurl='https://'
|
|
else
|
|
baseurl='http://'
|
|
fi
|
|
# APT does not follow http redirects so we need direct URL.
|
|
baseurl+="git.altlinux.org/tasks/archive/done/_$((task/1024))/$task/build/$subtask/$arch/rpms"
|
|
# shellcheck disable=SC2154
|
|
[[ -n "$kernel" ]] && to_plan "$name-debuginfo-$ver-$rel"
|
|
|
|
if [[ -n "$headers" ]]; then
|
|
to_plan "kernel-headers-$flavour-$ver-$rel"
|
|
[[ -n "$headers_modules" ]] && to_plan "kernel-headers-modules-$flavour-$ver-$rel"
|
|
fi
|
|
|
|
if [[ "$requested" -eq 0 ]]; then
|
|
echo "${MAGENTA}Nothing selected to install$NORM"
|
|
exit 0
|
|
elif [[ ! -v plan ]]; then
|
|
echo "${GREEN}It seems that everything as already installed$NORM"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -z "$quiet" ]]; then
|
|
echo
|
|
echo "Direct Internet access is required to download from the ALT Packages Archive"
|
|
echo -n "Do you allow ${YELLOW}Internet access$NORM [Y/n]? "
|
|
confirmator
|
|
echo
|
|
fi
|
|
|
|
CURL=$(type -p curl ||:)
|
|
declare -a urls
|
|
declare -i sumsize=0
|
|
[[ "${#plan[@]}" -gt 1 ]] && things='packages need' || things='package needs'
|
|
echo "The following $things to be installed:"
|
|
for url in "${plan[@]}"; do
|
|
url="$baseurl/$url.$arch.rpm"
|
|
urls+=( "$url" )
|
|
# Add space for curl errors.
|
|
printf " %s " "$url"
|
|
if [[ "$quiet" ]] || [[ -z "$CURL" ]]; then
|
|
# Postpone Internet access until final confirmation.
|
|
echo
|
|
continue
|
|
fi
|
|
filesize=$($CURL -sSfL --head "$url" | grep -ioP '(?<=Content-Length:\s)[0-9]+')
|
|
[[ -n "$filesize" ]] || fatal "${cr}Unknown filesize (aboring)"
|
|
filesize=$((filesize/1000000))
|
|
printf " [%d MB]\n" "$filesize"
|
|
sumsize+=$filesize
|
|
done
|
|
echo
|
|
|
|
[[ "$dryrun" =~ download-only ]] && action='download' || action='install'
|
|
printf "Proceed to ${YELLOW}$action${NORM} %d package%s" "${#urls[@]}" "$([ ${#urls[@]} -gt 1 ] && echo s)"
|
|
[[ "$sumsize" -gt 0 ]] && printf " taking $BRIGHT%d MB$NORM" "$sumsize"
|
|
printf " [Y/n]? "
|
|
confirmator
|
|
echo
|
|
|
|
[[ "$UID" = "0" ]] || message "This tool needs ${YELLOW}root privileges${NORM}."
|
|
( set -x;
|
|
# shellcheck disable=SC2086
|
|
apt-get install $dryrun $reinstall "${urls[@]}") || fatal "failed to install the packages"
|