Fix rpm and dnf package spec issues

When attempting to check if a package exists for a particular provide string
what happened is if an older version of the package was on the system but a
newer version in the repositories then the older (system) version would be
returned by dnf provides, but this could not be translated into a package name
with dnf repoquery because that specific version could not be found in the
repos.  The solution was to check rpm first to translate the package name on the
system and then dnf repoquery if rpm doesn't find it.

This brought to light issues when passing arrays of package names to rpm and dnf
which might happen to contain an empty element.  In this case rpm and dnf would
consider the empty arg an indicator that it should match all packages on the
system, or all available packages.  While we should try to avoid passing arrays
with empty elements, this highlighed a need to make rpm and dnf safer in this
regard, and so there are now saferpm and sfednf functions which simply strip any
empty args before calling the appropriate command with the rest of the args
untouched.

This commit also fixes an issue with output column formatting.
This commit is contained in:
Peter Ajamian 2021-06-04 22:11:15 +12:00
parent ef1cfd1fb9
commit 844896ad6b

View File

@ -228,11 +228,36 @@ provides_pkg () (
provides=$(dnf -q provides "$1" | awk '{print $1; nextfile}') || provides=$(dnf -q provides "$1" | awk '{print $1; nextfile}') ||
return 1 return 1
set +o pipefail set +o pipefail
pkg=$(dnf -q repoquery --queryformat '%{NAME}' "$provides") || pkg=$(rpm -q --queryformat '%{NAME}\n' "$provides") ||
pkg=$(dnf -q repoquery --queryformat '%{NAME}\n' "$provides") ||
exit_message "Can't get package name for $provides." exit_message "Can't get package name for $provides."
printf '%s\n' "$pkg" printf '%s\n' "$pkg"
) )
# If you pass an empty arg as one of the package specs to rpm it will match
# every package on the system. This funtion simply strips out any empty args
# and passes the rest to rpm to avoid this side-effect.
saferpm () (
args=()
for a in "$@"; do
if [[ $a ]]; then
args+=("$a")
fi
done
rpm "${args[@]}"
)
# And a similar function for dnf
safednf () (
args=()
for a in "$@"; do
if [[ $a ]]; then
args+=("$a")
fi
done
dnf "${args[@]}"
)
collect_system_info () { collect_system_info () {
# Check the efi mount first, so we can bail before wasting time on all these # Check the efi mount first, so we can bail before wasting time on all these
# other checks if it's not there. # other checks if it's not there.
@ -285,8 +310,8 @@ collect_system_info () {
done done
printf '%s\n' '' '' "Found the following repositories which map from $PRETTY_NAME to Rocky Linux 8:" printf '%s\n' '' '' "Found the following repositories which map from $PRETTY_NAME to Rocky Linux 8:"
column -t -N "$PRETTY_NAME,Rocky Linux 8" < <(for r in "${!repo_map[@]}"; do column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for r in "${!repo_map[@]}"; do
printf '%s %s\n' "${repo_map[$r]}" "$r" printf '%s\t%s\n' "${repo_map[$r]}" "$r"
done) done)
printf '\n%s' "${blue}Getting system package names for $PRETTY_NAME$nocolor." printf '\n%s' "${blue}Getting system package names for $PRETTY_NAME$nocolor."
@ -344,26 +369,27 @@ collect_system_info () {
done done
printf '%s\n' '' '' "Found the following system packages which map from $PRETTY_NAME to Rocky Linux 8:" printf '%s\n' '' '' "Found the following system packages which map from $PRETTY_NAME to Rocky Linux 8:"
column -t -N "$PRETTY_NAME,Rocky Linux 8" < <(for p in "${!pkg_map[@]}"; do column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for p in "${!pkg_map[@]}"; do
printf '%s %s\n' "${pkg_map[$p]}" "$p" printf '%s\t%s\n' "${pkg_map[$p]}" "$p"
done) done)
printf '%s\n' '' "${blue}Getting list of installed system packages$nocolor." printf '%s\n' '' "${blue}Getting list of installed system packages$nocolor."
readarray -t installed_packages < <(rpm -qa --queryformat="%{NAME}\n" "${pkg_map[@]}")
readarray -t installed_packages < <(saferpm -qa --queryformat="%{NAME}\n" "${pkg_map[@]}")
declare -g -A installed_pkg_check installed_pkg_map declare -g -A installed_pkg_check installed_pkg_map
for p in "${installed_packages[@]}"; do for p in "${installed_packages[@]}"; do
installed_pkg_check[$p]=1 installed_pkg_check[$p]=1
done done
for p in "${!pkg_map[@]}"; do for p in "${!pkg_map[@]}"; do
if [[ ${installed_pkg_check[${pkg_map[$p]}]} ]]; then if [[ ${pkg_map[$p]} && ${installed_pkg_check[${pkg_map[$p]}]} ]]; then
installed_pkg_map[$p]=${pkg_map[$p]} installed_pkg_map[$p]=${pkg_map[$p]}
fi fi
done; done;
printf '%s\n' '' "We will replace the following $PRETTY_NAME packages with their Rocky Linux 8 equivalents" printf '%s\n' '' "We will replace the following $PRETTY_NAME packages with their Rocky Linux 8 equivalents"
column -t -N "Packages to be Removed,Packages to be Installed" < <( column -t -s $'\t' -N "Packages to be Removed,Packages to be Installed" < <(
for p in "${!installed_pkg_map[@]}"; do for p in "${!installed_pkg_map[@]}"; do
printf '%s %s\n' "${installed_pkg_map[$p]}" "$p" printf '%s\t%s\n' "${installed_pkg_map[$p]}" "$p"
done done
) )
@ -383,7 +409,7 @@ collect_system_info () {
# Get a list of system enabled modules. # Get a list of system enabled modules.
readarray -t enabled_modules < <( readarray -t enabled_modules < <(
set -e -o pipefail set -e -o pipefail
dnf -q "${repo_map[@]/#/--repo=}" module list --enabled | safednf -q "${repo_map[@]/#/--repo=}" module list --enabled |
awk ' awk '
$1 == "@modulefailsafe", /^$/ {next} $1 == "@modulefailsafe", /^$/ {next}
$1 == "Name", /^$/ {if ($1!="Name" && !/^$/) print $1":"$2} $1 == "Name", /^$/ {if ($1!="Name" && !/^$/) print $1":"$2}
@ -448,7 +474,7 @@ package_swaps() {
done done
# Use dnf shell to swap the system packages out. # Use dnf shell to swap the system packages out.
dnf -y shell --disablerepo=\* --noautoremove \ safednf -y shell --disablerepo=\* --noautoremove \
--setopt=protected_packages= --setopt=keepcache=True \ --setopt=protected_packages= --setopt=keepcache=True \
"${dnfparameters[@]}" \ "${dnfparameters[@]}" \
<<EOF <<EOF
@ -470,7 +496,7 @@ EOF
# linux package will be removed and then installed again. # linux package will be removed and then installed again.
local -a check_removed check_installed local -a check_removed check_installed
readarray -t check_removed < <( readarray -t check_removed < <(
rpm -qa --qf '%{NAME}\n' "${installed_pkg_map[@]}" \ saferpm -qa --qf '%{NAME}\n' "${installed_pkg_map[@]}" \
"${addl_pkg_removes[@]}" | sort -u "${addl_pkg_removes[@]}" | sort -u
) )
@ -478,9 +504,13 @@ EOF
printf '%s\n' '' "${blue}Packages found on system that should still be removed. Forcibly removing them with rpm:$nocolor" printf '%s\n' '' "${blue}Packages found on system that should still be removed. Forcibly removing them with rpm:$nocolor"
# Removed packages still found on the system. Forcibly remove them. # Removed packages still found on the system. Forcibly remove them.
for pkg in "${check_removed[@]}"; do for pkg in "${check_removed[@]}"; do
# Extra safety measure, skip if empty string
if [[ -z $pkg ]]; then
continue
fi
printf '%s\n' "$pkg" printf '%s\n' "$pkg"
rpm -e --allmatches --nodeps "${check_removed[@]}" || saferpm -e --allmatches --nodeps "$pkg" ||
rpm -e --allmatches --nodeps --noscripts --notriggers "$pkg" saferpm -e --allmatches --nodeps --noscripts --notriggers "$pkg"
done done
fi fi
@ -488,7 +518,7 @@ EOF
readarray -t check_installed < <( readarray -t check_installed < <(
{ {
printf '%s\n' "${!installed_pkg_map[@]}" | sort -u printf '%s\n' "${!installed_pkg_map[@]}" | sort -u
rpm -qa --qf '%{NAME}\n' "${!installed_pkg_map[@]}" | sort -u saferpm -qa --qf '%{NAME}\n' "${!installed_pkg_map[@]}" | sort -u
} | sort | uniq -u } | sort | uniq -u
) )
if (( ${#check_installed[@]} )); then if (( ${#check_installed[@]} )); then
@ -537,7 +567,7 @@ EOF
printf '%s\n' '' "${blue}Removing dnf cache$nocolor" printf '%s\n' '' "${blue}Removing dnf cache$nocolor"
rm -rf /var/cache/{yum,dnf} rm -rf /var/cache/{yum,dnf}
printf '%s\n' "${blue}Ensuring repos are enabled before the package swap$nocolor" printf '%s\n' "${blue}Ensuring repos are enabled before the package swap$nocolor"
dnf -y config-manager --set-enabled "${!repo_map[@]}" || { safednf -y config-manager --set-enabled "${!repo_map[@]}" || {
printf '%s\n' 'Repo name missing?' printf '%s\n' 'Repo name missing?'
exit 25 exit 25
} }
@ -545,24 +575,24 @@ EOF
if (( ${#managed_repos[@]} )); then if (( ${#managed_repos[@]} )); then
# Filter the managed repos for ones still in the system. # Filter the managed repos for ones still in the system.
readarray -t managed_repos < <( readarray -t managed_repos < <(
dnf -q repolist "${managed_repos[@]}" | awk '$1!="repo" {print $1}' safednf -q repolist "${managed_repos[@]}" | awk '$1!="repo" {print $1}'
) )
if (( ${#managed_repos[@]} )); then if (( ${#managed_repos[@]} )); then
printf '%s\n' '' "${blue}Disabling subscription managed repos$nocolor." printf '%s\n' '' "${blue}Disabling subscription managed repos$nocolor."
dnf -y config-manager --disable "${managed_repos[@]}" safednf -y config-manager --disable "${managed_repos[@]}"
fi fi
fi fi
if (( ${#enabled_modules[@]} )); then if (( ${#enabled_modules[@]} )); then
printf '%s\n' "${blue}Enabling modules$nocolor" '' printf '%s\n' "${blue}Enabling modules$nocolor" ''
dnf -y module enable "${enabled_modules[@]}" || safednf -y module enable "${enabled_modules[@]}" ||
exit_message "Can't enable modules ${enabled_modules[*]}" exit_message "Can't enable modules ${enabled_modules[*]}"
fi fi
# Make sure that excluded repos are disabled. # Make sure that excluded repos are disabled.
printf '%s\n' "${blue}Disabling excluded modules$nocolor" '' printf '%s\n' "${blue}Disabling excluded modules$nocolor" ''
dnf -y module disable "${module_excludes[@]}" || safednf -y module disable "${module_excludes[@]}" ||
exit_message "Can't disable modules ${module_excludes[*]}" exit_message "Can't disable modules ${module_excludes[*]}"
printf '%s\n' '' "${blue}Syncing packages$nocolor" '' printf '%s\n' '' "${blue}Syncing packages$nocolor" ''