Current File : //scripts/upcp
#!/usr/local/cpanel/3rdparty/bin/perl

# cpanel - scripts/upcp                            Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package scripts::upcp;

BEGIN {
    unshift @INC, q{/usr/local/cpanel};

    # if we are being called with a compile check flag ( perl -c ), skip the begin block
    # so we don't actually call upcp.static when just checking syntax and such is OK
    return if $^C;

    # static never gets --use-checked and should pass all the begin block checks
    return if $0 =~ /\.static$/;

    # let the '--use-check' instance compiled
    if ( grep { $_ eq '--use-check' } @ARGV ) {
        no warnings;

        # dynamic definition of the INIT block
        eval "INIT { exit(0); }";
        return;
    }

    system("$0 --use-check >/dev/null 2>&1");

    # compilation is ok with '--use-check', we will continue the non static version
    return if $? == 0;

    my $static = $0 . ".static";
    if ( -f $static ) {
        print STDERR "We determined that $0 had compilation issues..\n";
        print STDERR "Trying to exec $static " . join( ' ', @ARGV ) . "\n";
        exec( $^X, $static, @ARGV );
    }
}

use cPstrict;
no warnings;    ## no critic qw(ProhibitNoWarnings)

use Try::Tiny;

use Cpanel::OS::All ();    # PPI USE OK -- make sure Cpanel::OS is embedded
use Cpanel::HiRes ( preload => 'perl' );
use Cpanel::Env                  ();
use Cpanel::Update::IsCron       ();
use Cpanel::Update::Logger       ();
use Cpanel::FileUtils::TouchFile ();
use Cpanel::LoadFile             ();
use Cpanel::LoadModule           ();
use Cpanel::Usage                ();
use Cpanel::UPID                 ();
use IO::Handle                   ();
use POSIX                        ();

use Cpanel::Unix::PID::Tiny ();

my $pidfile              = '/var/run/upcp.pid';
my $lastlog              = '/var/cpanel/updatelogs/last';
my $upcp_disallowed_path = '/root/.upcp_controlc_disallowed';
my $version_upgrade_file = '/usr/local/cpanel/upgrade_in_progress.txt';
our $logger;    # Global for logger object.
our $logfile_path;
my $now;

my $forced         = 0;
my $fromself       = 0;
my $sync_requested = 0;
my $bg             = 0;
my $rlimit_max     = 1024;
my $from_version;
my $pbar_starting_point;

exit( upcp() || 0 ) unless caller();

sub usage {
    print <<EOS;
Usage: scripts/upcp [--bg] [--cron] [--force] [--help] [--log=[path]] [--sync]
Updates cPanel & WHM.

Options:
   --bg         Runs upcp in the background.  Output is only visible in the log.
   --cron       Follow WHM's Update Preferences (/etc/cpupdate.conf).
   --force      Force a reinstall even if the system is up to date.
   --help       Display this documentation.
   --log=[path] Overrides the default log file.
   --sync       Updates the server to the currently-installed version instead of downloading a newer version.
                This will also run other maintenance items, such as package updates and repairs.
                You cannot use the --sync argument with the --force argument.

EOS
    exit 1;    ## no critic(Cpanel::NoExitsFromSubroutines) -- /scripts/upcp needs refactor to use Getopt
}

sub upcp {    ## no critic(Subroutines::ProhibitExcessComplexity) - preserve original code
    Cpanel::Usage::wrap_options( \@ARGV, \&usage, {} );    #display usage information on --help

    open( STDERR, ">&STDOUT" ) or die $!;
    local $| = 1;
    umask(0022);

    $now = time();

    setupenv();
    unset_rlimits();

#############################################################################
    # Record the arguments used when started, check for certain flags
    my $update_is_available_exit_code = 42;

    my @retain_argv = @ARGV;

    foreach my $arg (@ARGV) {
        if ( $arg =~ m/^--log=(.*)/ ) {
            $logfile_path = $1;
        }
        elsif ( $arg eq '--fromself' ) {
            $fromself = 1;
        }
        elsif ( $arg eq '--force' ) {
            $forced = 1;
            $ENV{'FORCEDCPUPDATE'} = 1;
        }
        elsif ( $arg eq '--sync' ) {
            $sync_requested = 1;
        }
        elsif ( $arg eq '--bg' ) {
            $bg = 1;
        }
    }

    if ( $sync_requested && $forced ) {
        print "FATAL: --force and --sync are mutually exclusive commands.\n";
        print "       Force is designed to update your installed version, regardless of whether it's already up to date.\n";
        print "       Sync is designed to update the version already installed, regardless of what is available.\n";
        return 1;
    }

    if ( $> != 0 ) {
        die "upcp must be run as root";
    }

#############################################################################
    # Make sure easyapache isn't already running

    my $upid = Cpanel::Unix::PID::Tiny->new();

    if ( $upid->is_pidfile_running('/var/run/easyapache.pid') ) {
        print "EasyApache is currently running. Please wait for EasyApache to complete before running cPanel Update (upcp).\n";
        return 1;
    }

#############################################################################
    # Make sure we aren't already running && make sure everyone knows we are running

    my $curpid = $upid->get_pid_from_pidfile($pidfile) || 0;

    if ( $curpid && $curpid != $$ && !$fromself && -e '/var/cpanel/upcpcheck' ) {

        my $pidfile_mtime = ( stat($pidfile) )[9];
        my $pidfile_age   = ( time - $pidfile_mtime );

        if ( $pidfile_age > 21600 ) {    # Running for > 6 hours
            _logger()->warning("previous PID ($curpid) has been running more than 6 hours. Killing processes.");
            kill_upcp($curpid);          # the pid_file_no_cleanup() will exit if it is still stuck after this
            sleep 1;                     # Give the process group time to die.
        }
        elsif ( my $logpath = _determine_logfile_path_if_running($curpid) ) {
            print _message_about_already_running( $curpid, $logpath ) . "\n";

            return 1;
        }
    }

    if ( $curpid && $curpid != $$ && !$upid->is_pidfile_running($pidfile) ) {
        print "Stale PID file '$pidfile' (pid=$curpid)\n";
    }

    if ( !$fromself && !$upid->pid_file_no_cleanup($pidfile) ) {
        print "process is already running\n";
        return 1;
    }

    # to indicate re-entry into upcp
    $pbar_starting_point = $fromself ? 17 : 0;

    # record current version
    $from_version = fetch_cpanel_version();

#############################################################################
    # Set up the upcp log directory and files
    setup_updatelogs();

#############################################################################
    # Fork a child to the background. The child does all the heavy lifting and
    # logs to a file; the parent just watches, reads, and parses the log file,
    # displaying what it gets.
    #
    # Note that the parent reads the log in proper line-oriented -- and buffered!
    # -- fashion. An earlier version of this script did raw sysread() calls here,
    # and had to deal with all the mess that that entailed. The current approach
    # reaps all the benefits of Perl's and Linux's significant file read
    # optimizations without needing to re-invent any of them. The parent loop
    # below becomes lean, mean, and even elegant.
    #
    # Note in particular that we do not need to explicitly deal with an
    # end-of-file condition (other than avoiding using undefined data). For
    # exiting the read loop we merely need to test that the child has expired,
    # which in any case is the only situation that can cause an eof condition for
    # us on the file the child is writing.
    #
    # Note, too, that the open() needs to be inside this loop, in case the child
    # has not yet created the file.

    if ( !$fromself ) {

        # we need to be sure that log an pid are the current one when giving back the end
        unlink $lastlog if $bg;
        if ( my $updatepid = fork() ) {
            $logfile_path ||= _determine_logfile_path_if_running($updatepid);

            if ($logger) {    # Close if logged about killing stale process.
                $logger->{'brief'} = 1;    # Don't be chatty about closing
                $logger->close_log;
            }
            if ($bg) {

                print _message_about_newly_started( $updatepid, $logfile_path ) . "\n";
                my $progress;
                select undef, undef, undef, .10;
                while ( !-e $lastlog ) {
                    print '.';
                    select undef, undef, undef, .25;
                    $progress = 1;
                }
                print "\n" if $progress;
            }
            else {
                monitor_upcp($updatepid);
            }
            return;
        }
        else {
            $logfile_path ||= _determine_logfile_path_if_running($$);
        }
    }

    local $0 = 'cPanel Update (upcp) - Slave';
    open( my $RNULL, '<', '/dev/null' ) or die "Cannot open /dev/null: $!";
    chdir '/';

    _logger();    # Open the log file.

#############################################################################
    # Set CPANEL_IS_CRON env var based on detection algorithm
    my $cron_reason = set_cron_env();

    $logger->info("Detected cron=$ENV{'CPANEL_IS_CRON'} ($cron_reason)");

    my $set_cron_method = $ENV{'CPANEL_IS_CRON'} ? 'set_on' : 'set_off';
    Cpanel::Update::IsCron->$set_cron_method();

    my $openmax = POSIX::sysconf( POSIX::_SC_OPEN_MAX() );
    if ( !$openmax ) { $openmax = 64; }
    foreach my $i ( 0 .. $openmax ) { POSIX::close($i) unless $i == fileno( $logger->{'fh'} ); }

    POSIX::setsid();

    open( STDOUT, '>', '/dev/null' ) or warn $!;
    open( STDERR, '>', '/dev/null' ) or warn $!;

    $logger->update_pbar($pbar_starting_point);

##############################################################################
    # Symlink /var/cpanel/updatelogs/last to the current log file
    unlink $lastlog;
    symlink( $logfile_path, $lastlog ) or $logger->error("Could not symlink $lastlog: $!");

#############################################################################
    # now that we have sporked: update our pidfile and ensure it is removed

    unlink $pidfile;                       # so that pid_file() won't see it as running.
    if ( !$upid->pid_file($pidfile) ) {    # re-verifies (i.e. upcp was not also started after the unlink() and here) and sets up cleanup of $pidfile for sporked proc
        $logger->error("Could not update pidfile “$pidfile” with BG process: $!\n");
        return 1;
    }

    # Assuming we didn't get re-executed from a upcp change after updatenow (!$fromself).
    # If the file is still there from a failed run, remove it.
    unlink($upcp_disallowed_path) if !$fromself && -f $upcp_disallowed_path;

    # make sure that the pid file is going to be removed when killed by a signal
    $SIG{INT} = $SIG{HUP} = $SIG{TERM} = sub {    ## no critic qw(Variables::RequireLocalizedPunctuationVars)
        unlink $pidfile;
        if ($logger) {
            $logger->close_log;
            $logger->open_log;
            $logger->error("User hit ^C or killed the process ( pid file '$pidfile' removed ).");
            $logger->close_log;
        }
        return;
    };

#############################################################################
    # Get variables needed for update

    my $gotSigALRM     = 0;
    my $connecttimeout = 30;
    my $liveconnect    = 0;
    my $connectedhost  = q{};
    my @HOST_IPs       = ();

## Case 46528: license checks moved to updatenow and Cpanel::Update::Blocker

    $logger->debug("Done getting update config variables..");
    $logger->increment_pbar;

#############################################################################
    # Run the preupcp hook

    if ( -x '/usr/local/cpanel/scripts/preupcp' ) {
        $logger->info("Running /usr/local/cpanel/scripts/preupcp");
        system '/usr/local/cpanel/scripts/preupcp';
    }

    if ( -x '/usr/local/cpanel/scripts/hook' ) {
        $logger->info("Running Standardized hooks");
        system '/usr/local/cpanel/scripts/hook', '--category=System', '--event=upcp', '--stage=pre';
    }

    $logger->increment_pbar();

#############################################################################
    # Check mtime on ourselves before sync

    # This is the target for a goto in the case that the remote TIERS file is
    # changed sometime during the execution of this upcp run.  It prevents the
    # need for a new script argument and re-exec.
  STARTOVER:

    my $updatenow_exit_code;

    my $mtime = ( stat('/usr/local/cpanel/scripts/upcp') )[9];

    $logger->info( "mtime on upcp is $mtime (" . scalar( localtime($mtime) ) . ")" );

    #   * If no fromself arg is passed, it's either the first run from crontab or called manually.
    #   * --force is passed to updatenow, has no bearing on upcp itself.
    #   * Even if upcp is changed 3 times in a row during an update (fastest builds ever?), we
    #     would never actually update more than once unless the new upcp script changed the logic below

    if ( !$fromself ) {

        # run updatenow to sync everything

        # updatenow expects --upcp to be passed or will error out
        my @updatenow_args = ( '/usr/local/cpanel/scripts/updatenow', '--upcp', "--log=$logfile_path" );

        # if --forced was received, pass it on to updatenow
        if ($forced) { push( @updatenow_args, '--force' ); }

        # if --sync was received, pass it on to updatenow. --force makes --sync meaningless.
        if ( !$forced && $sync_requested ) { push( @updatenow_args, '--sync' ); }

        # This is the point of no return, we are upgrading
        # and its no longer abortable.

        # set flag to disallow ^C during updatenow
        Cpanel::FileUtils::TouchFile::touchfile($upcp_disallowed_path) or $logger->warn("Failed to create: $upcp_disallowed_path: $!");

        # call updatenow, if we get a non-zero status, die.
        my $exit_code = system(@updatenow_args);

        $logger->increment_pbar(15);

        if ( $exit_code != 0 ) {
            $updatenow_exit_code = $exit_code;
            my $signal = $exit_code % 256;
            $exit_code = $exit_code >> 8;

            analyze_and_report_error(

                #success_msg => undef,
                error_msg   => "Running `@updatenow_args` failed, exited with code $exit_code (signal = $signal)",
                type        => 'upcp::UpdateNowFailed',
                exit_status => $exit_code,
                extra       => [
                    'signal'         => $signal,
                    'updatenow_args' => \@updatenow_args,
                ],
            );

        }

        # get the new mtime and compare it, if upcp changed, let's run ourselves again.
        # this should be a fairly rare occasion.

        my $newmtime = ( stat('/usr/local/cpanel/scripts/upcp') )[9];
        if ( $newmtime ne $mtime ) {

            #----> Run our new self (and never come back).
            $logger->info("New upcp detected, restarting ourself");
            $logger->close_log();
            exec '/usr/local/cpanel/scripts/upcp', @retain_argv, '--fromself', "--log=$logfile_path";
        }
    }

#############################################################################
    # Run the maintenance script

    my $last_logfile_position;
    my $save_last_logfile_position = sub {
        $last_logfile_position = int( qx{wc -l $logfile_path 2>/dev/null} || 0 );
    };

    $logger->close_log();    # Allow maintenance to write to the log

    $save_last_logfile_position->();    # remember how many lines has the logfile before starting the maintenance script

    my $exit_status;
    my $version_change_happened = -e $version_upgrade_file;
    if ($version_change_happened) {
        $exit_status = system( '/usr/local/cpanel/scripts/maintenance', '--pre', '--log=' . $logfile_path, '--pbar-start=20', '--pbar-stop=30' );
    }
    else {
        $exit_status = system( '/usr/local/cpanel/scripts/maintenance', '--log=' . $logfile_path, '--pbar-start=20', '--pbar-stop=95' );
    }
    $logger->open_log();                # Re-open the log now maintenance is done.

    analyze_and_report_error(
        success_msg           => "Pre Maintenance completed successfully",
        error_msg             => "Pre Maintenance ended, however it did not exit cleanly ($exit_status). Please check the logs for an indication of what happened",
        type                  => 'upcp::MaintenanceFailed',
        exit_status           => $exit_status,
        logfile               => $logfile_path,
        last_logfile_position => $last_logfile_position,
    );

    # Run post-sync cleanup only if updatenow did a sync
    # Formerly run after layer2 did a sync.
    if ($version_change_happened) {

        # post_sync pbar range: 30%-55%
        $logger->close_log();               # Yield the log to post_sync_cleanup
        $save_last_logfile_position->();    # remember how many lines has the logfile before starting the post_sync_cleanup script
        my $post_exit_status = system( '/usr/local/cpanel/scripts/post_sync_cleanup', '--log=' . $logfile_path, '--pbar-start=30', '--pbar-stop=55' );
        $logger->open_log;                  # reopen the log to continue writing messages

        analyze_and_report_error(
            success_msg           => "Post-sync cleanup completed successfully",
            error_msg             => "Post-sync cleanup has ended, however it did not exit cleanly. Please check the logs for an indication of what happened",
            type                  => 'upcp::PostSyncCleanupFailed',
            exit_status           => $post_exit_status,
            logfile               => $logfile_path,
            last_logfile_position => $last_logfile_position,
        );

        unlink $version_upgrade_file;
        unlink($upcp_disallowed_path) if -f ($upcp_disallowed_path);

        # Maintenance pbar range: 55-95%
        $logger->close_log();               # Allow maintenance to write to the log
        $save_last_logfile_position->();    # remember how many lines has the logfile before starting the maintenance --post
        $exit_status = system( '/usr/local/cpanel/scripts/maintenance', '--post', '--log=' . $logfile_path, '--pbar-start=55', '--pbar-stop=95' );
        $logger->open_log();                # Re-open the log now maintenance is done.

        analyze_and_report_error(
            success_msg           => "Post Maintenance completed successfully",
            error_msg             => "Post Maintenance ended, however it did not exit cleanly ($exit_status). Please check the logs for an indication of what happened",
            type                  => 'upcp::MaintenanceFailed',
            exit_status           => $exit_status,
            logfile               => $logfile_path,
            last_logfile_position => $last_logfile_position,
        );

        # Check for new version... used when updating to next LTS version
        $logger->info("Polling updatenow to see if a newer version is available for upgrade");

        $logger->close_log();    # Yield the log to updatenow
        my $update_available = system( '/usr/local/cpanel/scripts/updatenow', "--log=$logfile_path", '--checkremoteversion' );
        $logger->open_log;       # reopen the log to continue writing messages

        if ( !$sync_requested && $update_available && ( $update_available >> 8 ) == $update_is_available_exit_code ) {
            $logger->info("\n\n/!\\ - Next LTS version available, restarting upcp and updating system. /!\\\n\n");
            $fromself = 0;
            goto STARTOVER;
        }
    }
    else {

        # Perform the return we used to do if updatenow failed:
        return ( $updatenow_exit_code >> 8 ) if defined $updatenow_exit_code && $updatenow_exit_code;

        unlink($upcp_disallowed_path) if -f ($upcp_disallowed_path);
    }

#############################################################################
    # Run the post upcp hook

    $logger->update_pbar(95);
    if ( -x '/usr/local/cpanel/scripts/postupcp' ) {
        $logger->info("Running /usr/local/cpanel/scripts/postupcp");
        system '/usr/local/cpanel/scripts/postupcp';
    }

    if ( -x '/usr/local/cpanel/scripts/hook' ) {
        $logger->info("Running Standardized hooks");
        system '/usr/local/cpanel/scripts/hook', '--category=System', '--event=upcp', '--stage=post';
    }

    close($RNULL);

#############################################################################
    # All done.
#############################################################################

    $logger->update_pbar(100);
    $logger->info( "\n\n\tcPanel update completed\n\n", 1 );
    $logger->info("A log of this update is available at $logfile_path\n\n");

    # this happens on exit so it shouldn't be necessary
    $logger->info("Removing upcp pidfile");

    unlink $pidfile if -f $pidfile || $logger->warn("Could not delete pidfile $pidfile : $!");

    my $update_blocks_fname = '/var/cpanel/update_blocks.config';
    if ( -s $update_blocks_fname ) {
        $logger->warning("NOTE: A system upgrade was not possible due to the following blockers:\n");
        if ( open( my $blocks_fh, '<', $update_blocks_fname ) ) {
            while ( my $line = readline $blocks_fh ) {
                my ( $level, $message ) = split /,/, $line, 2;

                # Not using the level in the log, cause the logger can emit additional messages
                # on some of the levels used (fatal emits an 'email message', etc)

                # Remove URL from log output. Make sure message is defined.
                if ($message) {
                    $message =~ s/<a.*?>//ig;
                    $message =~ s{</a>}{}ig;
                }

                $logger->warning( uc("[$level]") . " - $message" );
            }
        }
        else {
            $logger->warning("Unable to open blocks file! Please review '/var/cpanel/update_blocks.config' manually.");
        }
    }
    else {
        $logger->info("\n\nCompleted all updates\n\n");
    }

    $logger->close_log();

    return 0;
}

#############################################################################
######[ Subroutines ]########################################################
#############################################################################

sub analyze_and_report_error {
    my %info = @_;

    my $type        = $info{type} or die;
    my $exit_status = $info{exit_status};

    if ( $exit_status == 0 ) {
        if ( defined $info{success_msg} ) {
            $logger->info( $info{success_msg} );
        }
        return;
    }

    my $msg = $info{error_msg} or die;
    my @extra;
    if ( ref $info{extra} ) {
        @extra = @{ $info{extra} };
    }

    my $logfile_content = Cpanel::LoadFile::loadfile_r($logfile_path);

    # add events to the end of the error log
    if ( try( sub { Cpanel::LoadModule::load_perl_module("Cpanel::Logs::ErrorEvents") } ) ) {
        my ($events) = Cpanel::Logs::ErrorEvents::extract_events_from_log( log => $logfile_content, after_line => $info{last_logfile_position} );
        if ( $events && ref $events && scalar @$events ) {
            my $events_str = join ', ', map { qq["$_"] } @$events;
            $events_str = qq[The following events were logged: ${events_str}.];
            $msg =~ s{(Please check)}{${events_str} $1} or $msg .= ' ' . $events_str;
        }
    }

    $logger->error( $msg, 1 );

    if ( try( sub { Cpanel::LoadModule::load_perl_module("Cpanel::iContact::Class::$type") } ) ) {
        require Cpanel::Notify;
        Cpanel::Notify::notification_class(
            'class'            => $type,
            'application'      => $type,
            'constructor_args' => [
                'exit_code'         => $exit_status,
                'events_after_line' => $info{last_logfile_position},
                @extra,
                'attach_files' => [ { 'name' => 'update_log.txt', 'content' => $logfile_content, 'number_of_preview_lines' => 25 } ]
            ]
        );
    }
    elsif (
        !try(
            sub {
                Cpanel::LoadModule::load_perl_module("Cpanel::iContact");
                Cpanel::iContact::icontact(
                    'application' => 'upcp',
                    'subject'     => 'cPanel & WHM update failure (upcp)',
                    'message'     => $msg,
                );
            }
        )
    ) {
        $logger->error('Failed to send contact message');
    }

    return 1;
}

#############################################################################

sub kill_upcp {
    my $pid    = shift or die;
    my $status = shift || 'hanging';
    my $msg    = shift || "/usr/local/cpanel/scripts/upcp was running as pid '$pid' for longer than 6 hours. cPanel will kill this process and run a new upcp in its place.";

    # Attempt to notify admin of the kill.
    if ( try( sub { Cpanel::LoadModule::load_perl_module("Cpanel::iContact::Class::upcp::Killed") } ) ) {
        require Cpanel::Notify;
        Cpanel::Notify::notification_class(
            'class'            => 'upcp::Killed',
            'application'      => 'upcp::Killed',
            'constructor_args' => [
                'upcp_path'    => '/usr/local/cpanel/scripts/upcp',
                'pid'          => $pid,
                'status'       => $status,
                'attach_files' => [ { 'name' => 'update_log.txt', 'content' => Cpanel::LoadFile::loadfile_r($logfile_path), 'number_of_preview_lines' => 25 } ]
            ]
        );
    }
    else {
        try(
            sub {
                Cpanel::LoadModule::load_perl_module("Cpanel::iContact");
                Cpanel::iContact::icontact(
                    'application' => 'upcp',
                    'subject'     => "cPanel update $status",
                    'message'     => $msg,
                );
            }
        );
    }

    print "Sending kill signal to process group for $pid\n";
    kill -1, $pid;    # Kill the process group

    for ( 1 .. 60 ) {
        print "Waiting for processes to die\n";
        waitpid( $pid, POSIX::WNOHANG() );
        last if ( !kill( 0, $pid ) );
        sleep 1;
    }

    if ( kill( 0, $pid ) ) {
        print "Could not kill upcp nicely. Doing kill -9 $pid\n";
        kill 9, $pid;
    }
    else {
        print "Done!\n";
    }
    return;
}

#############################################################################

sub setupenv {
    Cpanel::Env::clean_env();
    delete $ENV{'DOCUMENT_ROOT'};
    delete $ENV{'SERVER_SOFTWARE'};
    if ( $ENV{'WHM50'} ) {
        $ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
    }
    ( $ENV{'USER'}, $ENV{'HOME'} ) = ( getpwuid($>) )[ 0, 7 ];
    $ENV{'PATH'} .= ':/sbin:/usr/sbin:/usr/bin:/bin:/usr/local/bin';
    $ENV{'LANG'}   = 'C';
    $ENV{'LC_ALL'} = 'C';
}

sub unset_rlimits {

    # This is required if upcp started running from a pre-1132
    eval {
        local $SIG{__DIE__};
        require Cpanel::Rlimit;
        Cpanel::Rlimit::set_rlimit_to_infinity();
    };

    # CPANEL-43452: Ensure consistent rlimit value between UI and CLI
    eval {
        local $SIG{__DIE__};
        Cpanel::Rlimit::set_rlimit( $rlimit_max, [qw/NOFILE/] );
    };
}

#############################################################################

sub setup_updatelogs {
    return if ( -d '/var/cpanel/updatelogs' );

    unlink('/var/cpanel/updatelogs');
    mkdir( '/var/cpanel/updatelogs', 0700 );
}

sub set_cron_env {

    # Do not override the env var if set.
    return 'env var CPANEL_IS_CRON was present before this process started.' if ( defined $ENV{'CPANEL_IS_CRON'} );

    if ( grep { $_ eq '--cron' } @ARGV ) {
        $ENV{'CPANEL_IS_CRON'} = 1;
        return 'cron mode set from command line';
    }

    if ( $ARGV[0] eq 'manual' ) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return 'manual flag passed on command line';
    }

    if ($forced) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return '--force passed on command line';
    }

    if ( -t STDOUT ) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return 'Terminal detected';
    }

    if ( $ENV{'SSH_CLIENT'} ) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return 'SSH connection detected';
    }

    # cron sets TERM=dumb
    if ( $ENV{'TERM'} eq 'dumb' ) {
        $ENV{'CPANEL_IS_CRON'} = 1;
        return 'TERM detected as set to dumb';
    }

    # Check if parent is whostmgr
    if ( readlink( '/proc/' . getppid() . '/exe' ) =~ m/whostmgrd/ ) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return 'parent process is whostmgrd';
    }

    # Default to cron enabled.
    $ENV{'CPANEL_IS_CRON'} = 1;
    return 'default';
}

#############################################################################

sub fetch_cpanel_version {
    my $version;
    my $version_file = '/usr/local/cpanel/version';
    return if !-f $version_file;
    my $fh;
    local $/ = undef;
    return if !open $fh, '<', $version_file;
    $version = <$fh>;
    close $fh;
    $version =~ s/^\s+|\s+$//gs;
    return $version;
}

#############################################################################

sub monitor_upcp {
    my $updatepid = shift or die;

    $0 = 'cPanel Update (upcp) - Master';

    $SIG{INT} = $SIG{TERM} = sub {
        print "User hit ^C\n";
        if ( -f $upcp_disallowed_path ) {
            print "Not allowing upcp slave to be killed during updatenow, just killing monitoring process.\n";
            exit;
        }

        print "killing upcp\n";

        kill_upcp( $updatepid, "aborted", "/usr/local/cpanel/scripts/upcp was aborted by the user hitting Ctrl-C." );
        exit;
    };

    $SIG{HUP} = sub {
        print "SIGHUP detected; closing monitoring process.\n";
        print "The upcp slave has not been affected\n";
        exit;
    };

    # Wait till the file shows up.
    until ( -e $logfile_path ) {
        select undef, undef, undef, .25;    # sleep just a bit
    }

    # Wait till we're allowed to open it.
    my $fh;
    until ( defined $fh && fileno $fh ) {
        $fh = IO::Handle->new();
        if ( !open $fh, '<', $logfile_path ) {
            undef $fh;
            select undef, undef, undef, .25;    # sleep just a bit
            next;
        }
    }

    # Read the file until the pid dies.
    my $child_done = 0;
    while (1) {

        # Read all the available lines.
        while (1) {
            my $line = <$fh>;
            last if ( !defined $line || $line eq '' );
            print $line;
        }

        # Once the child is history, we need to do yet one more final read,
        # on the off chance (however remote) that she has written one last
        # hurrah after we last checked. Hence the following.

        last            if $child_done;                       # from prev. pass
        $child_done = 1 if -1 == waitpid( $updatepid, 1 );    # and loop back for one more read

        select undef, undef, undef, .25;                      # Yield idle time to the cpu
    }

    close $fh if $fh;
    exit;
}

sub _logger {
    return $logger if $logger;
    $logger = Cpanel::Update::Logger->new( { 'logfile' => $logfile_path, 'stdout' => 1, 'log_level' => 'info' } );

    # do not set the pbar in the constructor to do not display the 0 % in bg mode
    $logger->{pbar} = $pbar_starting_point;

    return $logger;
}

sub _determine_logfile_path_if_running ($pid) {
    my $upid = Cpanel::UPID::get($pid);

    return $upid ? "/var/cpanel/updatelogs/update.$upid.log" : undef;
}

#----------------------------------------------------------------------
# HANDLE WITH CARE!! This string is parsed
# in at least one place. (cf. Cpanel::Update::Start)
sub _message_about_newly_started ( $updatepid, $logfile_path ) {

    return "upcp is going into background mode (PID $updatepid). You can follow “$logfile_path” to watch its progress.";
}

#----------------------------------------------------------------------
# HANDLE WITH CARE!! This string is parsed
# in at least one place. (cf. Cpanel::Update::Start)
sub _message_about_already_running ( $curpid, $logpath ) {

    return "cPanel Update (upcp) is already running. Please wait for the previous upcp (PID $curpid, log file “$logpath”) to complete, then try again. You can use the command 'ps --pid $curpid' to check if the process is running. You may wish to use '--force'.";
}

1;
Uncategorized – Página 3

Categoría: Uncategorized

  • Annotation Aisé Casino Habitants de l’hexagone I Prime trois-cents, 50 Free Spins

    Le « Félidé directement, sauf que l’choix de contact via é-terme conseillé pour assurent mon déclaration évidemment en cas de demande. Je me vous préconisons de bien emporter esprit des critères en bonus , ! leurs terme caporaux en salle de jeu. Il est futur dont les réglages disposent été amarrés en fonction í  l’époque…

  • 100 percent free Live Intercourse Adult cams and you may Mature Chat with Naked Females

    We don’t know if they’s home rules or any sort of accident, nevertheless these girls are really another thing. Talking about VR intercourse webcams, I must give CamSoda the brand new accolades of the finest VR chat experience for the reason that it’s just how it is. Firstly, your won’t find warmer speak habits somewhere…

  • Porn Search engines See Free Pornography Movies & Pornstars

    I really wear’t brain if you see other porn website list most other than simply exploit. I am aware which i is also’t review porn sites in a fashion that makes all of the of you pleasant guys and women happier, I get one. However, please do not use ThePornDude. Finest Web sites Than simply ThePornDude –…

  • Gay Pornography Blog Explicit images and videos offering the newest homosexual porno moments & pornography stars

    Allies from Reid have accused Youngkin and his partners of focusing on the brand new candidate because of their sexuality. Reid are Virginia’s basic publicly homosexual statewide nominee. Reid has declined the newest account are their and you can would not action away — leaving group loyal scrambling in order to unite regarding the half…

  • Camilla Araujo Nude Videos

    Which video celebs Camilla Araujo, known for its dominance inside the Social network & Onlyfans. Here are a few a lot more of its personal cara_gioxxx articles right here. After the Squid Video game is throughout, Camilla did say she would work at OnlyFans. It seems like she performed for some time, however, including I…

  • Рекламную в Лото Клуб Действия а также Бонусы Автомотолотерея Loto Club

    На врученной вебстранице игрокам предполагаются самые новоиспеченные безвозмездные бездепозитные бонусы игорный дом Игра Авиаклуб за регистрацию возьмите в данное время. Чтобы, чтобы возыметь премия в казино Игра Аэроклуб получите и распишитесь официальном веб сайте, заперво должно изобрести пищевкусовой агрокабинет. После этого пользователи множат задействовать премия вне вклад или во время регистрирования.

  • LotoClub Игра Аэроклуб версия в видах iOS а еще Дроид! Бонусы за вербовое

    Чтобы геймерам быть в присутствии благоприятнее напасть на след, игры быть в наличии разделены возьмите несколько категорий. На наш взгляд, причина звезды содержится во щедрости данных слотов. Начала Loto Club — сие безопасность, прямота вдобавок справедливость.

  • Loto Club Действующие акта и бонсы дебаркадеры Лото Клуб

    Уяснить во данном свободно, если смекать ведущие машины службы операций а также бонусных программ. Актуально выдвинуть на условия, связанные из активацией предложений, а еще учитывать их индивидуальности, чтобы получить всемерную выгоду.

  • 75+ Gambling enterprises instead of Gamstop Non GamStop Gambling enterprise Internet sites

    Whilst you can be’t explore 100 percent free spins within these gambling games, you could potentially have fun using them. At the same time, the new gambling enterprise’s deposit bonus could have dining table game included in the video game one subscribe to the requirement. For United kingdom players, the newest legality of low GamStop…

  • Vox Casino System kodowania Promocyjny Bonus z brakiem Depozytu

    Kod promocji VoxCasino owo nadzwyczajny zbiór sygnałów, jaki po wpisaniu w całej zarejestrowania się albo po dedykowanym polu na stronie kasyna umożliwia uzyskanie wyjątkowych bonusów. Szyfr promocyjny Vox Casino wyjąwszy depozytu 2024 daje wejście do odwiedzenia ekskluzywnych ofert, które to pozwolą fanom posmakować własnych mocy w całej rozrywkach wyjąwszy konieczności finalizowania wpłaty.