Skip to content

Commit

Permalink
Merge pull request #528 from cpanel/RE-652
Browse files Browse the repository at this point in the history
Attempt to resolve duplicate RPMs
  • Loading branch information
toddr authored Oct 25, 2024
2 parents 3c626d1 + 56b3958 commit 1580d60
Show file tree
Hide file tree
Showing 7 changed files with 488 additions and 0 deletions.
160 changes: 160 additions & 0 deletions elevate-cpanel
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ BEGIN { # Suppress load of all of these at earliest point.
$INC{'Elevate/Components/NixStats.pm'} = 'script/elevate-cpanel.PL.static';
$INC{'Elevate/Components/OVH.pm'} = 'script/elevate-cpanel.PL.static';
$INC{'Elevate/Components/PackageRestore.pm'} = 'script/elevate-cpanel.PL.static';
$INC{'Elevate/Components/PackageDupes.pm'} = 'script/elevate-cpanel.PL.static';
$INC{'Elevate/Components/Panopta.pm'} = 'script/elevate-cpanel.PL.static';
$INC{'Elevate/Components/PECL.pm'} = 'script/elevate-cpanel.PL.static';
$INC{'Elevate/Components/PerlXS.pm'} = 'script/elevate-cpanel.PL.static';
Expand Down Expand Up @@ -102,6 +103,9 @@ BEGIN { # Suppress load of all of these at earliest point.

use constant ELEVATE_BACKUP_DIR => "/root/.elevate.backup";

use constant RPMDB_DIR => q[/var/lib/rpm];
use constant RPMDB_BACKUP_DIR => q[/var/lib/rpm-elevate-backup];

use constant IMUNIFY_AGENT => '/usr/bin/imunify360-agent';

use constant CHKSRVD_SUSPEND_FILE => q[/var/run/chkservd.suspend];
Expand Down Expand Up @@ -332,6 +336,7 @@ BEGIN { # Suppress load of all of these at earliest point.
use Elevate::Components::NixStats ();
use Elevate::Components::OVH ();
use Elevate::Components::PackageRestore ();
use Elevate::Components::PackageDupes ();
use Elevate::Components::Panopta ();
use Elevate::Components::PECL ();
use Elevate::Components::PerlXS ();
Expand Down Expand Up @@ -394,6 +399,7 @@ BEGIN { # Suppress load of all of these at earliest point.
NixStats
PECL
PackageRestore
PackageDupes
Panopta
PerlXS
PostgreSQL
Expand Down Expand Up @@ -4351,6 +4357,151 @@ EOS

} # --- END lib/Elevate/Components/PackageRestore.pm

{ # --- BEGIN lib/Elevate/Components/PackageDupes.pm

package Elevate::Components::PackageDupes;

use cPstrict;

use Digest::SHA ();

use Elevate::Constants ();

use Cpanel::SafeRun::Simple ();
use Cpanel::Version::Compare::Package ();

# use Log::Log4perl qw(:easy);
INIT { Log::Log4perl->import(qw{:easy}); }

# use Elevate::Components::Base();
our @ISA;
BEGIN { push @ISA, qw(Elevate::Components::Base); }

use constant { ALPHA => 0, DIGIT => 1 };

sub pre_distro_upgrade ($self) {

INFO('Looking for duplicate system packages...');
my %dupes = $self->_find_dupes();
if ( scalar %dupes > 0 ) {

INFO('Duplicates found.');
if ( !-d Elevate::Constants::RPMDB_BACKUP_DIR ) {
INFO('Backing up system package database. If there are problems upgrading packages, consider restoring this backup and resolving the duplicate packages manually.');
if ( $self->_backup_rpmdb() ) {
INFO( 'Active RPM database: ' . Elevate::Constants::RPMDB_DIR );
INFO( 'Backup RPM database: ' . Elevate::Constants::RPMDB_BACKUP_DIR );
}
else {
ERROR('The backup process did not produce a correct backup! ELevate will proceed with the next step in the upgrade process without attempting to correct the issue. If there are problems upgrading packages, resolve the duplicate packages manually.');
return;
}
}

my @packages_to_remove = $self->_select_packages_for_removal(%dupes);

DEBUG( "The following packages are being removed from the system package database:\n" . join( "\n", @packages_to_remove ) );
$self->_remove_packages(@packages_to_remove);
}
else {
INFO('No duplicate packages found.');
}

return;
}

sub _find_dupes ($self) {
my %dupes;
my $output = Cpanel::SafeRun::Simple::saferunnoerror(qw( /usr/bin/package-cleanup --dupes ));

foreach my $line ( split /\n/, $output ) {
my ( $name, $version, $release, $arch ) = _parse_package($line);
push $dupes{$name}->@*, { version => $version, release => $release, arch => $arch } if $name;
}

return %dupes;
}

sub _parse_package ($pkg) {
return ( $pkg =~ m/^(.+)-(.+)-(.+)\.(.+)$/ );
}

sub _backup_rpmdb ($self) {
my ( $orig_dir, $backup_dir ) = ( Elevate::Constants::RPMDB_DIR, Elevate::Constants::RPMDB_BACKUP_DIR );
rename $orig_dir, $backup_dir or LOGDIE("Failed to move $orig_dir to $backup_dir (reason: $!)");

File::Copy::Recursive::dircopy( $backup_dir, $orig_dir );
if ( !_rpmdb_backup_is_good( $orig_dir, $backup_dir ) ) {
restore_rpmdb_from_backup();
return 0;
}

return 1;
}

sub _rpmdb_backup_is_good ( $orig_dir, $backup_dir ) {

opendir( my $orig_dh, $orig_dir ) or return 0;
opendir( my $backup_dh, $backup_dir ) or return 0;

my @orig_files = sort grep { !/^\./ } readdir($orig_dh);
my @backup_files = sort grep { !/^\./ } readdir($backup_dh);

return 0 if scalar @orig_files != scalar @backup_files;

while ( scalar @orig_files && scalar @backup_files ) {
my ( $orig_file, $backup_file ) = ( shift(@orig_files), shift(@backup_files) );
return 0 if $orig_file ne $backup_file;

my ( $orig_digest, $backup_digest ) = map { Digest::SHA->new(256)->addfile($_)->hexdigest } ( "$orig_dir/$orig_file", "$backup_dir/$backup_file" );
return 0 if !$orig_digest || !$backup_digest || $orig_digest ne $backup_digest;
}

return 1;
}

sub restore_rpmdb_from_backup () {

local $SIG{'HUP'} = 'IGNORE';
local $SIG{'TERM'} = 'IGNORE';
local $SIG{'INT'} = 'IGNORE';
local $SIG{'QUIT'} = 'IGNORE';
local $SIG{'USR1'} = 'IGNORE';
local $SIG{'USR2'} = 'IGNORE';

my ( $orig_dir, $backup_dir ) = ( Elevate::Constants::RPMDB_DIR, Elevate::Constants::RPMDB_BACKUP_DIR );

File::Path::rmtree($orig_dir);
rename $backup_dir, $orig_dir or LOGDIE("Failed to restore original RPM database to $orig_dir (reason: $!)! It is currently stored at $backup_dir.");

return;
}

sub _select_packages_for_removal ( $self, %dupes ) {
my @pkgs_for_removal;

for my $pkg ( keys %dupes ) {
my @sorted_versions = sort { Cpanel::Version::Compare::Package::version_cmp( $a->{version}, $b->{version} ) || Cpanel::Version::Compare::Package::version_cmp( $a->{release}, $b->{release} ) } $dupes{$pkg}->@*;

splice @sorted_versions, -2, 1;

push @pkgs_for_removal, sprintf( '%s-%s-%s.%s', $pkg, $_->@{qw( version release arch )} ) foreach @sorted_versions;
}

return @pkgs_for_removal;
}

sub _remove_packages ( $self, @packages ) {
foreach my $pkg (@packages) {
$self->rpm->remove_no_dependencies_or_scripts_and_justdb($pkg);
}
return;
}

1;

} # --- END lib/Elevate/Components/PackageDupes.pm

{ # --- BEGIN lib/Elevate/Components/Panopta.pm

package Elevate::Components::Panopta;
Expand Down Expand Up @@ -7639,6 +7790,11 @@ EOS
return;
}

sub remove_no_dependencies_or_scripts_and_justdb ( $self, $pkg ) {
$self->cpev->ssystem( $rpm, '-e', '--nodeps', '--noscripts', '--justdb', $pkg );
return;
}

sub get_installed_rpms ( $self, $format = undef ) {
my @args = qw{-qa};

Expand Down Expand Up @@ -8658,6 +8814,7 @@ use Elevate::Components::NICs ();
use Elevate::Components::NixStats ();
use Elevate::Components::OVH ();
use Elevate::Components::PackageRestore ();
use Elevate::Components::PackageDupes ();
use Elevate::Components::Panopta ();
use Elevate::Components::PECL ();
use Elevate::Components::PerlXS ();
Expand Down Expand Up @@ -9332,6 +9489,9 @@ sub run_stage_2 ($self) {

$self->run_component_once( 'Grub2' => 'verify_cmdline' );

# Best to do this before clearing the yum cache:
$self->run_component_once( 'PackageDupes' => 'pre_distro_upgrade' );

$self->ssystem(qw{/usr/bin/yum clean all});
$self->ssystem_and_die(qw{/scripts/update-packages});
$self->ssystem_and_die(qw{/usr/bin/yum -y update});
Expand Down
2 changes: 2 additions & 0 deletions lib/Elevate/Components.pm
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use Elevate::Components::NICs ();
use Elevate::Components::NixStats ();
use Elevate::Components::OVH ();
use Elevate::Components::PackageRestore ();
use Elevate::Components::PackageDupes ();
use Elevate::Components::Panopta ();
use Elevate::Components::PECL ();
use Elevate::Components::PerlXS ();
Expand Down Expand Up @@ -112,6 +113,7 @@ our @NOOP_CHECKS = qw{
NixStats
PECL
PackageRestore
PackageDupes
Panopta
PerlXS
PostgreSQL
Expand Down
Loading

0 comments on commit 1580d60

Please sign in to comment.