#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - ea_current_to_profile Copyright(c) 2022 cPanel, Inc. # All rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited use strict; use warnings; package ea_cpanel_tools::ea_current_to_profile; use Cpanel::PackMan; use Cpanel::Config::Httpd; use Cpanel::JSON; use Cpanel::Time::Local; use Path::Tiny 'path'; our $ea4_profiles_dir = '/etc/cpanel/ea4/profiles'; our $_max_attempts = 20; our $manifest_file = "/etc/cpanel/ea4/profiles/pkg-manifest.json"; our $ea4_metainfo_file = '/etc/cpanel/ea4/ea4-metainfo.json'; our %safe_to_ignore = ( 'ea-brotli' => 1, 'ea-brotli-devel' => 1, 'ea-libargon2' => 1, 'ea-libargon2-devel' => 1, 'ea-libcurl' => 1, 'ea-libcurl-devel' => 1, 'ea-libnghttp2' => 1, 'ea-libxml2' => 1, 'ea-libzip' => 1, 'ea-nghttp2' => 1, 'ea-oniguruma' => 1, 'ea-oniguruma-devel' => 1, 'ea-openssl11' => 1, 'ea-openssl11-devel' => 1, 'ea-openssl11-libs' => 1, 'ea-openssl' => 1, 'ea-openssl-devel' => 1, 'ea-openssl-libs' => 1, 'ea-php73-libc-client' => 1, 'ea-php74-libc-client' => 1, 'ea-php80-libc-client' => 1, 'ea-php81-libc-client' => 1 ); our %os_type = ( "CentOS_7" => "rpm", "CentOS_8" => "rpm", "CentOS_9" => "rpm", "xUbuntu_20.04" => "deb", "xUbuntu_22.04" => "deb", ); exit( script(@ARGV) ) unless caller(); sub script { my (@args) = @_; my $custom_json; my $new_name; my $target_os; my $manifest_os; my $modified = 0; my $source_pkmgr = -x '/usr/bin/apt' ? 'deb' : 'rpm'; # We need to get OS aliases my %ea4_metainfo = %{ Cpanel::JSON::LoadFile($ea4_metainfo_file) }; my %obs_project_aliases = %{ $ea4_metainfo{obs_project_aliases} }; die "obs_project_aliases is empty" if ( !%obs_project_aliases ); foreach my $arg (@args) { if ( $arg eq "--help" ) { my $os_string = join( ' ', keys %obs_project_aliases ); print <basename } path("/etc/cpanel/ea4/additional-pkg-prefixes/")->children; }; die "May only be run if you are using EasyApache 4" if ( !Cpanel::Config::Httpd::is_ea4() ); my @pkgs_have = Cpanel::PackMan->instance->list( state => "installed", 'prefix' => 'ea-' ); @pkgs_have = grep !/ea-profiles-cpanel/, @pkgs_have; my $prefix_piped = "ea"; for my $prefix (@addl_prefixes) { push @pkgs_have, Cpanel::PackMan->instance->list( state => "installed", 'prefix' => "$prefix-" ); for my $ig ( keys %safe_to_ignore ) { $ig =~ s/^ea-/$prefix-/; $safe_to_ignore{$ig} = 1; } $prefix_piped .= "|$prefix"; } my $prefix_qr = qr/(?:$prefix_piped)/; my $custom_dir = $ea4_profiles_dir . "/custom"; mkdir $custom_dir if !-d $custom_dir; die "Cannot create $custom_dir" if !-d $custom_dir; my @tags; # this heuristic is fragile but at least attempts to create tags foreach my $pkg (@pkgs_have) { if ( $pkg =~ m/^($prefix_qr)-apache(\d)(\d)$/ ) { push( @tags, "Apache $2.$3 ($1)" ); } if ( $pkg =~ m/^($prefix_qr)-php(\d)(\d)$/ ) { push( @tags, "PHP $2.$3 ($1)" ); } if ( $pkg =~ m/^($prefix_qr)-php(\d)(\d)-opcache$/ ) { push( @tags, "PHP $2.$3 OpCache ($1)" ); } if ( $pkg =~ m/^($prefix_qr)-apache(\d)(\d).mod.(mpm_.*)$/ ) { push( @tags, "MPM $2.$3 $4 ($1)" ); } if ( $pkg =~ m/^($prefix_qr)-apache(\d)(\d).mod.(ruid.*)$/ ) { push( @tags, "$4 $2.$3 ($1)" ); } } my %extra_meta; if ($target_os) { $extra_meta{os_upgrade} = { source_os => scalar( _get_src_os() ), target_os => $target_os, target_obs_project => $manifest_os, dropped_pkgs => {}, # do it here so it is included even when there are no packages dropped }; my %manifest = %{ Cpanel::JSON::LoadFile($manifest_file) }; my %experimental = map { _normalize_pkg($_) => 1 } @{ $manifest{'EA4-experimental'}->{$manifest_os} }; # merge the repos to make lookup easy my %lookup; foreach my $repo ( keys %manifest ) { foreach my $pkg ( @{ $manifest{$repo}->{$manifest_os} } ) { my $normalized_pkg = _normalize_pkg($pkg); $lookup{$normalized_pkg} = 1; } } my @output_pkgs; my @removed_pkgs; foreach my $pkg (@pkgs_have) { my $npkg = _normalize_pkg($pkg); if ( !exists $lookup{$npkg} || exists $experimental{$npkg} ) { push( @removed_pkgs, $npkg ); $modified = 1; } else { push( @output_pkgs, $pkg ); } } if (@removed_pkgs) { my $flag = 0; foreach my $pkg (@removed_pkgs) { if ( !exists $safe_to_ignore{$pkg} && substr( $pkg, -10, 10 ) ne '-debuginfo' ) { print "The following packages are not available on $target_os and have been removed from the profile\n" if !$flag; print " $pkg"; print " (EA4-experimental packages are not preserved during OS upgrades)" if ( exists $experimental{$pkg} ); print "\n"; $flag = 1; $extra_meta{os_upgrade}{dropped_pkgs}{$pkg} = exists $experimental{$pkg} ? "exp" : "reg"; } } print "\n" if $flag; } @pkgs_have = @output_pkgs if (@removed_pkgs); } if ( !defined $custom_json ) { my $ts = Cpanel::Time::Local::localtime2timestamp(); $new_name = "Current EA4 State at " . $ts; my $fs_ts = substr( $ts, 0, 19 ); $fs_ts =~ s/ /_/g; $custom_json = $custom_dir . "/" . "current_state_at_" . $fs_ts . ".json"; if ( $modified && $target_os ) { $new_name .= " modified for $target_os" if ( $modified && $target_os ); my $xtarget_os = "_modified_for_$target_os"; $xtarget_os =~ s/ /_/g; $custom_json = $custom_dir . "/" . "current_state_at_" . $fs_ts . $xtarget_os . ".json"; } } my $custom_profile = { name => $new_name, desc => "Auto Generated profile", version => "1.0", pkgs => \@pkgs_have, tags => \@tags, %extra_meta, }; path($custom_json)->spew( Cpanel::JSON::pretty_dump($custom_profile) ); die "Could not create a new custom json file" if ( !-f $custom_json ); print $custom_json; return 0; # return is exit status } ############### #### helpers ## ############### sub _get_src_os { my $src_os = eval { require Cpanel::OS; Cpanel::OS::display_name(); }; $src_os //= `grep PRETTY_NAME /etc/os-release | sed 's/^PRETTY_NAME=//' | tr -d '"' | tr -d "'"`; chomp $src_os; $src_os ||= 'Unknown. Server does not have Cpanel::OS or /etc/os-release (or /etc/os-release does not have PRETTY_NAME=…)'; return $src_os; } sub _normalize_pkg { my ($pkg) = @_; $pkg =~ s/_/-/g; return $pkg; } 1;