Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to resolve duplicate RPMs #528

Merged
merged 2 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading