From: Daniel Molkentin Date: Mon, 25 Feb 2019 14:35:11 +0000 (+0100) Subject: purge-kernels: correctly decide wether to remove KMPs or livepatches X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3aac7df4b14f687ff53189879e66598443cbae95;p=thirdparty%2Fdracut.git purge-kernels: correctly decide wether to remove KMPs or livepatches There was a workaround added for kernels on which a package might depend and the kernel is removed from the list of kernels to remove when something depends on it. This however does not work if something depends on the kernel indirectly: old kernel - old kmp - old virtualbox In this case attempt to remove the kernel triggers removing the kmp and attempt removing the kmp fails because virtualbox depends on it. The old kmp is removed from the list of packages to remove and the operation is retried. However, the old kernel is still on the list of packages to remove and attempting to remove it adds the old kmp as well. Signed-Off-By: Michal Suchanek --- diff --git a/suse/purge-kernels b/suse/purge-kernels index 7bc0f3d0e..9d541e970 100644 --- a/suse/purge-kernels +++ b/suse/purge-kernels @@ -268,8 +268,37 @@ sub list_old_packages { return @packages; } +sub find_package { + my $name = shift @_; + my $version = shift @_; + my @packages = @_; + my $expr = "^" . quotemeta("$name-$version"); + my @found = grep { $_ =~ $expr } @packages; + return @found if @found; + $expr = "^" . quotemeta($name) . " = " . quotemeta($version) . "\$"; + @found = grep { + my @provides = qx/rpm -q --provides $_/; + chomp (@provides); + grep { $_ =~ $expr} @provides; + } @packages; + return @found; +} + +# Try to remove a list of packages. +# +# If there is a KMP or livepatch depending on the package remove it as well. If +# there is another package depending on the kernel keep the kernel. If there is +# a package that depends on a KMP keep the KMP and a kernel required to use the +# KMP. +# In each step a KMP or livepatch may be added or a package which cannot be +# removed due to dependencies is marked as taboo and removed from the list. +# +# Finish when packages uninstall successfully or we can't find any packages to +# add or remove from the list to make it uninstallable. + sub remove_packages { my @packages = @_; + my %taboo_packages; while (1) { pipe(my $read, my $write); @@ -296,25 +325,30 @@ sub remove_packages { print "Removed:\n ", join("\n ", @packages), "\n"; return 1; } - my ($retry, @problems); + my $retry = 0; my %old_packages = map { $_ => 1 } @packages; my %new_packages; for (@out) { if (/ is needed by \(installed\) (kernel-syms-.*|kgraft-patch-.*|kernel-livepatch-.*|.*-kmp-.*)/ && - !$old_packages{$1}) { + !$old_packages{$1} && !$taboo_packages{$1}) { push(@packages, $1) unless $new_packages{$1}; $new_packages{$1} = 1; $retry = 1; } elsif (/([^ \t]*) = ([^ \t]*) is needed by \(installed\) /) { - print STDERR "$0: $_\n"; - @packages = grep ! /$1-$2/, @packages; - $retry = 1; - } else { - push(@problems, $_); + my @unremovable = find_package($1, $2, @packages); + my $match = $unremovable[$#unremovable]; + if ($match) { + print STDERR "$0: $_\n"; + print STDERR "$0: Keeping $1 = $2 ($match)\n"; + @packages = grep { $_ !~ $match } @packages; + $taboo_packages{$match} = 1; + $retry = 1; + last; # Only remove one package providing the dependency from the list + } } } if (!$retry) { - print STDERR join("\n", @problems), "\n"; + print STDERR join("\n", @out), "\n"; print STDERR "$0: giving up.\n"; return 0; }