]> git.ipfire.org Git - thirdparty/dracut.git/commitdiff
purge-kernels: correctly decide wether to remove KMPs or livepatches
authorDaniel Molkentin <dmolkentin@suse.com>
Mon, 25 Feb 2019 14:35:11 +0000 (15:35 +0100)
committerDaniel Molkentin <dmolkentin@suse.com>
Mon, 25 Feb 2019 14:44:45 +0000 (15:44 +0100)
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 <msuchanek@suse.com>
suse/purge-kernels

index 7bc0f3d0e1f5407a430f63f8ea82189c617de041..9d541e970b07608cefdf26a5ec51ad185227a350 100644 (file)
@@ -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;
                }