From 844896ad6bd7c4a7dcea4b6015005f3368a060a5 Mon Sep 17 00:00:00 2001 From: Peter Ajamian Date: Fri, 4 Jun 2021 22:11:15 +1200 Subject: [PATCH] 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. --- migrate2rocky.sh | 70 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/migrate2rocky.sh b/migrate2rocky.sh index 18b591e..0d37ac9 100644 --- a/migrate2rocky.sh +++ b/migrate2rocky.sh @@ -228,11 +228,36 @@ provides_pkg () ( provides=$(dnf -q provides "$1" | awk '{print $1; nextfile}') || return 1 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." 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 () { # Check the efi mount first, so we can bail before wasting time on all these # other checks if it's not there. @@ -285,8 +310,8 @@ collect_system_info () { done 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 - printf '%s %s\n' "${repo_map[$r]}" "$r" + column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for r in "${!repo_map[@]}"; do + printf '%s\t%s\n' "${repo_map[$r]}" "$r" done) printf '\n%s' "${blue}Getting system package names for $PRETTY_NAME$nocolor." @@ -344,26 +369,27 @@ collect_system_info () { done 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 - printf '%s %s\n' "${pkg_map[$p]}" "$p" + column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for p in "${!pkg_map[@]}"; do + printf '%s\t%s\n' "${pkg_map[$p]}" "$p" done) 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 for p in "${installed_packages[@]}"; do installed_pkg_check[$p]=1 done 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]} fi done; 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 - printf '%s %s\n' "${installed_pkg_map[$p]}" "$p" + printf '%s\t%s\n' "${installed_pkg_map[$p]}" "$p" done ) @@ -383,7 +409,7 @@ collect_system_info () { # Get a list of system enabled modules. readarray -t enabled_modules < <( set -e -o pipefail - dnf -q "${repo_map[@]/#/--repo=}" module list --enabled | + safednf -q "${repo_map[@]/#/--repo=}" module list --enabled | awk ' $1 == "@modulefailsafe", /^$/ {next} $1 == "Name", /^$/ {if ($1!="Name" && !/^$/) print $1":"$2} @@ -448,7 +474,7 @@ package_swaps() { done # 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 \ "${dnfparameters[@]}" \ <