From 7c6ddf87961687b19ab155897faa9b75b22b190edd1862c0bf6604b979ea7243 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Wed, 18 Dec 2013 16:20:48 +0000 Subject: [PATCH 1/2] - Add purge kernel service/feature which before was part of mkinitrd bnc#854348 OBS-URL: https://build.opensuse.org/package/show/Base:System/dracut?expand=0&rev=84 --- dracut.changes | 6 + dracut.spec | 13 ++ purge-kernels | 374 ++++++++++++++++++++++++++++++++++++++++++ purge-kernels.service | 14 ++ 4 files changed, 407 insertions(+) create mode 100644 purge-kernels create mode 100644 purge-kernels.service diff --git a/dracut.changes b/dracut.changes index 46e25b4..5de03be 100644 --- a/dracut.changes +++ b/dracut.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Dec 18 16:20:04 UTC 2013 - trenn@suse.de + +- Add purge kernel service/feature which before was part of mkinitrd + bnc#854348 + ------------------------------------------------------------------- Thu Nov 28 09:56:36 CET 2013 - hare@suse.de diff --git a/dracut.spec b/dracut.spec index 0a0a7f7..5786b67 100644 --- a/dracut.spec +++ b/dracut.spec @@ -34,6 +34,8 @@ Source0: http://www.kernel.org/pub/linux/utils/boot/dracut/dracut-%{versi Source1: module-setup-initrd.sh Source2: parse-suse-initrd.sh Source3: mkinitrd_setup_dummy +Source4: purge-kernels +Source5: purge-kernels.service Patch0: dracut-git-update.patch # S/390 fixes, send to upstream @@ -207,14 +209,25 @@ install -m 0755 %{S:3} %{buildroot}/sbin/mkinitrd_setup mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d install -m 0644 dracut.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/dracut +install -D -m 0755 %{SOURCE4} %{buildroot}/sbin/purge-kernels +install -m 644 %{SOURCE5} $RPM_BUILD_ROOT/%{_unitdir}/purge-kernels.service + %clean rm -rf %{buildroot} +%pre +%service_add_pre purge-kernels.service + +%preun +%service_del_preun purge-kernels.service + %files %defattr(-,root,root,0755) %doc README HACKING TODO COPYING AUTHORS NEWS dracut.html dracut.png dracut.svg %{_bindir}/dracut %if %{replace_mkinitrd} +/sbin/purge-kernels +%{_unitdir}/purge-kernels.service /sbin/mkinitrd /sbin/mkinitrd_setup %{_bindir}/lsinitrd diff --git a/purge-kernels b/purge-kernels new file mode 100644 index 0000000..3320f9d --- /dev/null +++ b/purge-kernels @@ -0,0 +1,374 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Getopt::Long; + +sub usage { + print "Usage: $0 [--test]\n"; + print "Reads list of kernels to keep from /etc/zypp/zypp.conf:multiversion.kernels\n"; + print "kernels can be given as , latest(-N), running, oldest(+N).\n"; +} + +# arch/flavor => version-release => [ subpackages ] +my %kernels; +my %kmps; + +my @keep_spec; +my ($want_running, $running_version, $running_flavor); + +# do not actually delete anything +my $test_only; + +# undocumented debugging options +my ($fake_config, $fake_rpm_qa, $fake_uname_r, $fake_uname_m); + +sub get_config_line { + my $file = "/etc/zypp/zypp.conf"; + + if ($fake_config) { + return $fake_config; + } + if (!-e $file) { + print STDERR "$0: /etc/zypp/zypp.conf does not exist, exiting.\n"; + exit 0; + } + open(my $fh, '<', $file) or die "$0: $file: $!\n"; + while (<$fh>) { + chomp; + next unless /^\s*multiversion\.kernels\b/; + s/^[^=]*=\s*//; + close($fh); + return $_; + } + close($fh); + return ""; +} + +sub load_config { + my @kernels; + + @kernels = split(/,\s*/, get_config_line()); + for my $kernel (@kernels) { + if ($kernel =~ /^\s*(latest|oldest|running)(\s*[-+]\s*\d+)?\s*$/) { + my $new = { whence => $1, offset => $2 || 0 }; + $new->{offset} =~ s/\s*//g; + if ($new->{whence} eq "running") { + $want_running = 1; + } + push (@keep_spec, $new); + } elsif ($kernel =~ /^\d+\.\d+/) { + my $new = { version => $kernel }; + push (@keep_spec, $new); + } elsif ($kernel =~ /^\s*$/) { + next; + } else { + print STDERR "$0: Ignoring unknow kernel specification in\n"; + print STDERR "/etc/zypp/zypp.conf:multiversion.kernels: $kernel\n"; + } + } +} + +sub add_package { + my ($name, $vr, $arch) = @_; + my ($flavor, $table); + + #print STDERR "add_package: $name $vr $arch\n"; + if ($name eq "kernel-firmware" || $name eq "kernel-coverage") { + return; + } + if ($name =~ /^kernel-/) { + ($flavor = $name) =~ s/^kernel-//; + $table = \%kernels; + } elsif ($name =~ /-kmp-/) { + ($flavor = $name) =~ s/.*-kmp-//; + $table = \%kmps; + } + # Put all subpackages into the same group, except for + # kernel-source-{vanilla,rt}, which are packages on their own + if ($flavor !~ /^source/) { + $flavor =~ s/-.*//; # XXX: No dashes in flavor names + } + # kernel-devel is a subpackage of kernel-source + $flavor =~ s/^devel/source/; + $table->{"$arch/$flavor"} ||= {}; + $table->{"$arch/$flavor"}{$vr} ||= []; + push(@{$table->{"$arch/$flavor"}{$vr}}, "$name-$vr.$arch"); +} + +sub load_packages { + my $pipe; + + if ($fake_rpm_qa) { + open($pipe, '<', $fake_rpm_qa) or die "$fake_rpm_qa: $!\n"; + } else { + open($pipe, '-|', 'rpm', '-qa', '--qf', + '%{n} %{v}-%{r} %{arch}\n', 'kernel-*', '*-kmp-*') + or die "rpm: $!\n"; + } + while (<$pipe>) { + chomp; + my ($name, $vr, $arch) = split; + add_package($name, $vr, $arch); + } + close($pipe) +} + +sub sort_versions { + my @versions = @_; + + pipe (my $read, my $write); + my $pid = fork(); + if (!defined($pid)) { + die "Cannot fork: $!\n"; + } elsif ($pid == 0) { + # child + close($read); + open STDOUT, '>&', $write; + open(my $fh, '|-', "/usr/lib/rpm/rpmsort") or die "/usr/lib/rpm/rpmsort: $!\n"; + print $fh join("\n", @versions), "\n"; + close($fh); + die "rpmsort failed ($?)\n" if $? != 0; + + exit 0; + } + # parent + close($write); + @versions = <$read>; + chomp @versions; + close($read); + waitpid($pid, 0); + die "rpmsort failed ($?)\n" if $? != 0; + + return @versions; +} + +# return true if VER1 == VER2 or VER1 == (VER2 minus rebuild counter) +sub version_match { + my ($ver1, $ver2) = @_; + + return 1 if $ver1 eq $ver2; + + # copied from kernel-source/rpm/kernel-spec-macros + $ver2 =~ s/\.[0-9]+($|\.[^.]*[^.0-9][^.]*$)/$1/; + return $ver1 eq $ver2; +} + +sub list_old_versions { + my ($flavor) = @_; + + my $is_source = $flavor =~ /\/(source|syms)/; + my $kernels = $kernels{$flavor}; + my @versions = sort_versions(keys(%$kernels)); + my %idx = ( + oldest => 0, + latest => scalar(@versions) - 1, + ); + if ($want_running && ($running_flavor eq $flavor || $is_source)) { + for (my $i = scalar(@versions) - 1; $i >= 0; $i--) { + if (version_match($running_version, $versions[$i])) { + $idx{running} = $i; + last; + } + } + if (!exists($idx{running}) && !$is_source) { + print STDERR "$0: Running kernel $running_version-$running_flavor not installed.\n"; + print "NOT removing any packages for flavor $flavor.\n"; + return; + } + } + my %delete = map { $_ => 1 } @versions; + for my $keep (@keep_spec) { + if ($keep->{version}) { + for my $ver (@versions) { + if (version_match($keep->{version}, $ver)) { + $delete{$ver} = 0; + } + } + } elsif ($keep->{whence}) { + next unless exists($idx{$keep->{whence}}); + my $idx = $idx{$keep->{whence}}; + $idx += $keep->{offset}; + next unless $idx >= 0 && $idx < scalar(@versions); + $delete{$versions[$idx]} = 0; + } else { + die "??"; + } + } + return grep { $delete{$_} } @versions; +} + +sub package_exists { + my ($version, $archs, $flavors) = @_; + + for my $arch (@$archs) { + for my $flavor (@$flavors) { + my $config = "$arch/$flavor"; + if (exists($kernels{$config}) + && exists($kernels{$config}->{$version})) { + return 1; + } + } + } + return 0; +} + +sub list_old_packages { + my (@packages, @archs, @flavors); + my (@syms_flavors, @binary_flavors, @source_configs); + + # there are some inter-dependencies among the kernel packages, + # so we have to be careful + my %t = map { s:/.*::; $_ => 1 } keys(%kernels); + @archs = sort(keys(%t)); + %t = map { s:.*/::; $_ => 1 } keys(%kernels); + @flavors = sort(keys(%t)); + @syms_flavors = grep { /^syms/ } @flavors; + @binary_flavors = grep { !/^(source|syms)/ } @flavors; + @source_configs = grep { /\/source/ } sort(keys(%kernels)); + + for my $arch (@archs) { + for my $flavor (@syms_flavors) { + my $config = "$arch/$flavor"; + next unless exists($kernels{$config}); + my @versions = list_old_versions($config); + for my $ver (@versions) { + push(@packages, @{$kernels{$config}->{$ver}}); + delete($kernels{$config}->{$ver}); + } + } + for my $flavor (@binary_flavors) { + my $config = "$arch/$flavor"; + next unless exists($kernels{$config}); + my @versions = list_old_versions($config); + for my $ver (@versions) { + my @pacs = @{$kernels{$config}->{$ver}}; + my $remove_all = 1; + # do not remove kernel-$flavor-devel-$ver + # if kernel-syms-$ver still exists + if (grep { /-devel$/ } @pacs) { + my $syms = "syms"; + if ($flavor =~ /^rt/) { + $syms = "syms-rt"; + } + if (exists($kernels{$syms}->{$ver})) { + $remove_all = 0; + @pacs = grep { !/-devel$/ } + @pacs; + } + } + push(@packages, @pacs); + if ($remove_all) { + delete($kernels{$config}->{$ver}); + } + } + } + } + for my $config (@source_configs) { + my @versions = list_old_versions($config); + for my $ver (@versions) { + # Remove kernel-{devel,source} only if no other package + # of the same version exists + next if package_exists($ver, \@archs, \@binary_flavors); + push(@packages, @{$kernels{$config}->{$ver}}); + } + } + return @packages; +} + +sub remove_packages { + my @packages = @_; + + while (1) { + pipe(my $read, my $write); + my $pid = fork(); + if (!defined($pid)) { + die "Cannot fork: $!\n"; + } elsif($pid == 0) { + # child + close($read); + open STDOUT, '>&', $write; + open STDERR, '>&', $write; + $ENV{LC_ALL} = "C"; + my @cmd = qw(rpm -e); + push(@cmd, "--test") if $test_only; + exec(@cmd, @packages) or die "rpm: $!\n"; + } + # parent + close($write); + my @out = <$read>; + chomp @out; + close($read); + waitpid($pid, 0); + if ($? == 0) { + print "Removed:\n ", join("\n ", @packages), "\n"; + return 1; + } + my ($retry, @problems); + my %old_packages = map { $_ => 1 } @packages; + my %new_packages; + for (@out) { + if (/ is needed by \(installed\) (.*-kmp-.*)/ && + !$old_packages{$1}) { + push(@packages, $1) unless $new_packages{$1}; + $new_packages{$1} = 1; + $retry = 1; + } else { + push(@problems, $_); + } + } + if (!$retry) { + print STDERR join("\n", @problems), "\n"; + print STDERR "$0: giving up.\n"; + return 0; + } + } +} + +if (!GetOptions( + "h|help" => sub { usage(); exit; }, + "--test" => \$test_only, + "--fake-config=s" => \$fake_config, + "--fake-rpm-qa=s" => \$fake_rpm_qa, + "--fake-uname-r=s" => \$fake_uname_r, + "--fake-uname-m=s" => \$fake_uname_m)) { + usage(); + exit 1; +} +load_config(); +if (!@keep_spec) { + print STDERR "$0: multiversion.kernels not configured in /etc/zypp/zypp.conf, exiting.\n"; + exit 0; +} + +load_packages(); +if ($want_running) { + $running_version = $fake_uname_r ? $fake_uname_r : `uname -r`; + chomp($running_version); + ($running_flavor = $running_version) =~ s/.*-//; + $running_version =~ s/-[^-]*$//; + (my $release = $running_version) =~ s/.*-//; + $running_version =~ s/-[^-]*$//; + + # copied from kernel-source/rpm/mkspec + $running_version =~ s/\.0-rc/.rc/; + $running_version =~ s/-rc\d+//; + $running_version =~ s/-/./g; + + $running_version .= "-$release"; + + my $arch = $fake_uname_m ? $fake_uname_m : `uname -m`; + chomp($arch); + $arch =~ s/^i.86$/i586/; + $running_flavor = "$arch/$running_flavor"; +} +my @remove = list_old_packages(); +if (!@remove) { + print STDERR "$0: Nothing to do.\n"; + exit 0; +} +if (remove_packages(@remove)) { + exit 0; +} +exit 1; diff --git a/purge-kernels.service b/purge-kernels.service new file mode 100644 index 0000000..a6b07b4 --- /dev/null +++ b/purge-kernels.service @@ -0,0 +1,14 @@ +[Unit] +Description=Purge old kernels +After=local-fs.target +ConditionPathExists=/boot/do_purge_kernels + +[Service] +Type=oneshot +Nice=19 +IOSchedulingClass=idle +ExecStartPre=/bin/rm -f /boot/do_purge_kernels +ExecStart=/sbin/purge-kernels + +[Install] +WantedBy=multi-user.target From 09edde63e5ae847f0918b7838ed0ab7118b9de7eadd627a8e92dfbed8f660654 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 20 Dec 2013 14:18:58 +0000 Subject: [PATCH 2/2] - Cleanup: Remove %define replace_mkinitrd 1 and related conditionals in .spec file -> One either has to install dracut or mkinitrd and it will stay like that. OBS-URL: https://build.opensuse.org/package/show/Base:System/dracut?expand=0&rev=85 --- dracut.changes | 7 +++++++ dracut.spec | 15 --------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/dracut.changes b/dracut.changes index 5de03be..92d929a 100644 --- a/dracut.changes +++ b/dracut.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Dec 20 14:17:54 UTC 2013 - trenn@suse.de + +- Cleanup: Remove %define replace_mkinitrd 1 + and related conditionals in .spec file -> One either has to install + dracut or mkinitrd and it will stay like that. + ------------------------------------------------------------------- Wed Dec 18 16:20:04 UTC 2013 - trenn@suse.de diff --git a/dracut.spec b/dracut.spec index 5786b67..9214d93 100644 --- a/dracut.spec +++ b/dracut.spec @@ -18,8 +18,6 @@ %define dracutlibdir %{_prefix}/lib/dracut -%define replace_mkinitrd 1 - Name: dracut Version: 034 Release: 0 @@ -66,10 +64,8 @@ BuildRequires: asciidoc BuildRequires: docbook-xsl-stylesheets BuildRequires: libxslt BuildRequires: pkgconfig(systemd) >= 199 -%if %{replace_mkinitrd} Obsoletes: mkinitrd < 2.8.2 Provides: mkinitrd = 2.8.2 -%endif Requires: bash Requires: coreutils @@ -193,18 +189,11 @@ echo 'add_drivers+="autofs4"' >> %{buildroot}%{_sysconfdir}/dracut.conf.d/01-dis echo 'early_microcode="yes"' > %{buildroot}%{_sysconfdir}/dracut.conf.d/02-early-microcode.conf %endif rm %{buildroot}%{_bindir}/mkinitrd -%if !%{replace_mkinitrd} -rm %{buildroot}/etc/bash_completion.d/lsinitrd -rm %{buildroot}%{_bindir}/lsinitrd -rm %{buildroot}%{_mandir}/man8/mkinitrd* -rm %{buildroot}%{_mandir}/man1/lsinitrd* -%else # moved to /sbin mkdir -p %{buildroot}/sbin install -m 0755 mkinitrd-suse.sh %{buildroot}/sbin/mkinitrd mv %{buildroot}%{_mandir}/man8/mkinitrd-suse.8 %{buildroot}%{_mandir}/man8/mkinitrd.8 install -m 0755 %{S:3} %{buildroot}/sbin/mkinitrd_setup -%endif mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d install -m 0644 dracut.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/dracut @@ -225,14 +214,12 @@ rm -rf %{buildroot} %defattr(-,root,root,0755) %doc README HACKING TODO COPYING AUTHORS NEWS dracut.html dracut.png dracut.svg %{_bindir}/dracut -%if %{replace_mkinitrd} /sbin/purge-kernels %{_unitdir}/purge-kernels.service /sbin/mkinitrd /sbin/mkinitrd_setup %{_bindir}/lsinitrd %config /etc/bash_completion.d/lsinitrd -%endif %dir %{dracutlibdir} %dir %{dracutlibdir}/modules.d %{dracutlibdir}/dracut-functions.sh @@ -250,10 +237,8 @@ rm -rf %{buildroot} %endif %dir %{_sysconfdir}/dracut.conf.d %{_mandir}/man8/dracut.8* -%if %{replace_mkinitrd} %{_mandir}/man8/mkinitrd.8* %{_mandir}/man1/lsinitrd.1* -%endif %{_mandir}/man7/dracut.kernel.7* %{_mandir}/man7/dracut.cmdline.7* %{_mandir}/man7/dracut.bootup.7*