[ SEA-GHOST MINI SHELL]

Path : /proc/3/cwd/usr/local/bin/
FILE UPLOADER :
Current File : //proc/3/cwd/usr/local/bin/ea_current_to_profile

#!/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 <<USAGE;
    $0 [--help] [--output=profile_file] [--target-os=os]
        Take the current ea rpms and create a custom profile.
        Profile is written to /etc/cpanel/ea4/profiles/custom

        --output=profile_file, where the profile is written to.
           Note: forcefully overwrites the profile_file if it already
           exists.

        --target-os=os [$os_string]

USAGE

            exit 0;
        }
        elsif ( $arg =~ m/^--output=(.*)$/ ) {
            $custom_json = $1;
            my @path = split( /\//, $custom_json );
            $new_name = $path[-1];
        }
        elsif ( $arg =~ m/^--target-os=(.*)$/ ) {
            $target_os = $1;
            die "Not a valid OS ($target_os)" if !exists $obs_project_aliases{$target_os};
            $manifest_os = $obs_project_aliases{$target_os};
        }
        else {
            die "Unknown argument “$arg”\n";
        }
    }

    my @addl_prefixes = eval {
        map { $_->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;

SEA-GHOST - SHELL CODING BY SEA-GHOST