MOON
Server: Apache/2.2.31 (Unix) mod_ssl/2.2.31 OpenSSL/0.9.8e-fips-rhel5 mod_bwlimited/1.4
System: Linux csr818.wilogic.com 2.6.18-419.el5xen #1 SMP Fri Feb 24 22:50:37 UTC 2017 x86_64
User: digitals (531)
PHP: 5.4.45
Disabled: NONE
Upload Files
File: //proc/self/root/proc/self/root/scripts.20110531.215904.25158/upcp.1130.static
#!/usr/bin/perl

BEGIN { # Supress load of all of these at earliest point.
    $INC{'Cpanel/Env.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/WHMAPI.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Whostmgr/UI.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/MagicRevision.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/StatCache.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/App.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/ProcessCheck.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/SafeRun/Errors.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/SafeRun/Simple.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/Update/Logger.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/SafeDir/MK.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/Logger.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/Time.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/AccessIds.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/PwCache.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/SafeFile.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/RcsRecord.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/FindBin.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/SafeRun/InOut.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/Debug.pm'} = '/usr/local/cpanel/scripts/upcp.static';
    $INC{'Cpanel/AccessIds/SetUids.pm'} = '/usr/local/cpanel/scripts/upcp.static';
}

{ # --- BEGIN Cpanel/Env.pm
package Cpanel::Env;



our $VERSION = '1.4';
my %SAFE_ENV_VARS;

sub cleanenv {
    my %OPTS = @_;

    if ( !scalar keys %SAFE_ENV_VARS ) {
        %SAFE_ENV_VARS = map { $_ => undef } (
            'ALLUSERSPROFILE',
            'APPDATA',
            'CLIENTNAME',
            'COMMONPROGRAMFILES',
            'COMPUTERNAME',
            'COMSPEC',
            'CPANEL_IS_CRON',    # Denotes that one of the parent procs is upcp
            'DOCUMENT_ROOT',
            'FP_NO_HOST_CHECK',
            'HOMEDRIVE',
            'HOMEPATH',
            'LD_ASSUME_KERNEL',
            'LOGONSERVER',
            'LONGUSERS',
            'NEWWHMUPDATE',
            'NOWHOSTMRRES',      # Tells upcp to not bounce whostmgr until after it's done.
            'NUMBER_OF_PROCESSORS',
            'OS',
            'PATH',
            'PATHEXT',
            'PROCESSOR_ARCHITECTURE',
            'PROCESSOR_IDENTIFIER',
            'PROCESSOR_LEVEL',
            'PROCESSOR_REVISION',
            'PROGRAMFILES',
            'PROMPT',
            'SERVER_SOFTWARE',
            'SESSIONNAME',
            'SSH_CLIENT',
            'SYSTEMDRIVE',
            'SYSTEMROOT',
            'TEMP',
            'TERM',
            'TMP',
            'USERDOMAIN',
            'USERNAME',
            'USERPROFILE',
            'WINDIR',
        );
    }

    if ( defined $OPTS{'keep'} && ref $OPTS{'keep'} eq 'ARRAY' ) {
        @SAFE_ENV_VARS{ @{ $OPTS{'keep'} } } = undef;
    }
    if ( defined $OPTS{'delete'} && ref $OPTS{'delete'} eq 'ARRAY' ) {
        delete @SAFE_ENV_VARS{ @{ $OPTS{'delete'} } };
    }

    delete @ENV{ map { exists $SAFE_ENV_VARS{$_} ? '' : $_ } keys %ENV };
    if ( $OPTS{'http_purge'} ) {
        delete @ENV{ 'SERVER_SOFTWARE', 'DOCUMENT_ROOT' };
    }
}

sub set_safe_path {
    $ENV{'PATH'} = '/usr/local/jdk/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/lib/courier-imap/sbin:/usr/lib/courier-imap/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/usr/X11R6/bin:/root/bin:/opt/bin';
}

1;

} # --- END Cpanel/Env.pm


{ # --- BEGIN Cpanel/WHMAPI.pm
package Cpanel::WHMAPI;



our $VERSION = 1.0;

my $has_Whostmgr_UI = 0;

eval '
    local $SIG{__DIE__} = "DEFAULT";
    use Whostmgr::UI;
    $has_Whostmgr_UI=1;
    ';

sub setstatus {
    if ($has_Whostmgr_UI) { goto &Whostmgr::UI::setstatus; }
    if (@_) { print $_[0] . "\n"; }
}

sub setstatusdone {
    if ($has_Whostmgr_UI) { goto &Whostmgr::UI::setstatusdone; }
    if (@_) { print $_[0] . "\n"; }
}

sub clearstatus {
    if ($has_Whostmgr_UI) { goto &Whostmgr::UI::clearstatus; }
    if (@_) { print $_[0] . "\n"; }
}

sub status_cmd {
    my @CMD = @_;
    my $data;

    my $zpid = open( READCHLD, "-|" );
    if ($zpid) {
        while (<READCHLD>) {
            print;
            $data .= $_;
        }
        close(READCHLD);
    }
    else {
        open( STDERR, ">&STDOUT" );
        exec @CMD;
        exit(1);
    }
    return $data;
}

sub status_note {
    print $_[0];
    return $_[0];
}

1;

} # --- END Cpanel/WHMAPI.pm


{ # --- BEGIN Whostmgr/UI.pm
package Whostmgr::UI;


use strict;
# use Cpanel::MagicRevision ();

our $method = 'print';
our $nohtml = 0;

sub setstatus {
    local $| = 1;
    my $statusdata = shift;
    $statusdata =~ s/\n//g;
    my $safestatus = $statusdata;
    $safestatus =~ s/\'/\\\'/g;
    if (   !$nohtml
        && !( -t STDOUT || !defined $ENV{'GATEWAY_INTERFACE'} || $ENV{'GATEWAY_INTERFACE'} !~ m/CGI/i ) ) {
        if ( $method ne 'hide' ) {
            print "<script>if (self['update_ui_status']) { update_ui_status('$safestatus'); }</script>" . ( qq{ } x 4096 ) . "\n";
        }
        my $txt = qq{<table style="border-bottom: 1px #ccc dotted;"><tr><td width="100%"><pre>$statusdata...};
        $method eq 'print' ? print $txt : return $txt;
    }
    else {
        $method eq 'print' ? print qq{$statusdata...} : return qq{$statusdata...};
    }
    return '';
}

sub setstatusdone {
    local $| = 1;
    if (   !$nohtml
        && !( -t STDOUT || !defined $ENV{'GATEWAY_INTERFACE'} || $ENV{'GATEWAY_INTERFACE'} !~ /CGI/i ) ) {

        my $txt = qq{...Done</pre></td><td width="30"><img align=absmiddle src="} . Cpanel::MagicRevision::calculate_magic_url( '/cjt/images/icons/success.png', $ENV{'REQUEST_URI'}, '/usr/local/cpanel/whostmgr/docroot' ) . qq{"></td></tr></table>\n};
        $method eq 'print' ? print $txt : return $txt;

    }
    else {
        $method eq 'print' ? print qq{...Done\n} : return qq{...Done\n};
    }
    return '';
}

sub clearstatus {
    local $| = 1;
    if (   !$nohtml
        && !( -t STDOUT || !defined $ENV{'GATEWAY_INTERFACE'} || $ENV{'GATEWAY_INTERFACE'} !~ /CGI/i ) ) {
        if ( $method ne 'hide' ) { print "<script>if (self['clear_ui_status']) { clear_ui_status(); }</script>\n"; }
    }
    return '';
}

1;

} # --- END Whostmgr/UI.pm


{ # --- BEGIN Cpanel/MagicRevision.pm
package Cpanel::MagicRevision;


use Cwd               ();
# use Cpanel::StatCache ();
# use Cpanel::App       ();

my %URI_CACHE;
my $cpanel_binary_mtime;
my $cached_mtime_func;
 
sub MagicRevision_init { 
	if (exists $INC{'Cpanel/StatCache/Persistant.pm'}) {
		eval q{$cached_mtime_func=\&Cpanel::StatCache::Persistant::cachedmtime;}; #q{} to prevent it from getting in the Stash unless loaded
	} else {
		$cached_mtime_func=\&Cpanel::StatCache::cachedmtime;
	}
	*MagicRevision_init = sub {};
}

sub MagicRevision_uri {
    print calculate_theme_relative_magic_url(shift);
}

sub calculate_theme_relative_magic_url {
	MagicRevision_init() if !defined $cached_mtime_func;
	
    my $filename = shift;

    if ( exists $URI_CACHE{$filename} ) {
        return $URI_CACHE{$filename};
    }
    my $basepath;
    my $docroot = '/usr/local/cpanel/base';
    if ( $filename !~ /^\// ) {
        no warnings 'once';
        $basepath = ( $Cpanel::appname eq 'webmail' ? '/webmail/' : '/frontend/' ) . $Cpanel::CPDATA{'RS'} . '/';
    }
    my $file     = $docroot . $basepath . $filename;
    my $url      = $basepath . $filename;
    my $magicnum = ( ($Cpanel::appname eq 'webmail' || $Cpanel::appname eq 'cpanel') ? $cached_mtime_func->($file, ($cpanel_binary_mtime ||= Cpanel::StatCache::cachedmtime('/usr/local/cpanel/cpanel'))  )  : Cpanel::StatCache::cachedmtime($file) );
    
    $URI_CACHE{$filename} = '/cPanel_magic_revision_' . ( $magicnum || 0 ) . '/' . $url;
    $URI_CACHE{$filename} =~ s{//+}{/}g;
    return $URI_CACHE{$filename};
}

sub calculate_magic_url {
    my $uri = shift;
    if ( $uri =~ tr/\ \t\r\n\f'"_// ) {
        $uri =~ s/[\s\'\"]*$//;
        if ( $uri =~ /\/+cPanel_magic_revision_[\.\d]+\/+/ ) {
            $uri =~ s{//+}{/}g;    #important
            return $uri;
        }
    }
    return $URI_CACHE{$uri} if ( exists $URI_CACHE{$uri} );

    my $current_uri = shift || $ENV{'REQUEST_URI'} || '/scripts/command';
    my $docroot = shift || ( $Cpanel::App::appname =~ /whostmgr/i ? '/usr/local/cpanel/whostmgr/docroot' : '/usr/local/cpanel/base' );

    MagicRevision_init() if !defined $cached_mtime_func;

    $current_uri =~ s/\?.*//g;
    $current_uri =~ s/(?:\/cpsess\d*)?//g;
    $current_uri =~ s/\/+cPanel_magic_revision_[\.\d]+\/+/\//g;

    my $file;
    if ( $uri =~ m/^\// ) {
        $file = $docroot . $uri;
    }
    elsif ($current_uri) {
        my @PATH = split( /\/+/, $current_uri );
        pop(@PATH);
        my $uri_dir = join( '/', @PATH );
        $file = $docroot . '/' . $uri_dir . '/' . $uri;
    }
    else {
        $file = $docroot . '/' . $uri;
    }

    $file =~ s{//+}{/}g;

    if ( !( ( $Cpanel::appname eq 'webmail' || $Cpanel::appname eq 'cpanel' ) ? $cached_mtime_func->( $file, ( $cpanel_binary_mtime ||= Cpanel::StatCache::cachedmtime('/usr/local/cpanel/cpanel') ) ) : Cpanel::StatCache::cachedmtime($file) ) ) {
        return $uri;
    }
    if ( $file =~ /\.{1,2}\// ) {
        $file = Cwd::abs_path($file);
    }

    $file =~ s/^\Q$docroot\E//g;
    $file = $docroot . $file;

    my $magic_revision_prefix = get_magic_revision_prefix($file);

    return $uri if !$magic_revision_prefix;

    $file =~ s/^\Q$docroot\E\/?//g;

    $URI_CACHE{$uri} = $magic_revision_prefix . '/' . $file;
    $URI_CACHE{$uri} =~ s{//+}{/}g;    #important

    return $URI_CACHE{$uri};
}

my %_Cached_Prefixes;
sub get_magic_revision_prefix {
	MagicRevision_init() if !defined $cached_mtime_func;
	
    my $file = shift() || return;

    return $_Cached_Prefixes{ $file } if defined $_Cached_Prefixes{ $file };

    my $mtime = ( $Cpanel::appname eq 'webmail' || $Cpanel::appname eq 'cpanel' )
        ? $cached_mtime_func->($file)  #TODO: fix spelling
        : Cpanel::StatCache::cachedmtime($file)
    ;

    if ( $mtime ) {
        return $_Cached_Prefixes{ $file } = '/cPanel_magic_revision_' . $mtime;
    }
    else {
        return;
    }
}

sub _readablecache {
	*_readablecache = \&Cpanel::StatCache::cachedmtime;
    goto &Cpanel::StatCache::cachedmtime;
}

1;

} # --- END Cpanel/MagicRevision.pm


{ # --- BEGIN Cpanel/StatCache.pm
package Cpanel::StatCache;


our $VERSION = 0.3;

my %STATCACHE;

sub StatCache_init { }

sub cachedmtime {
    return (
        exists $STATCACHE{ $_[0] } ?
        $STATCACHE{ $_[0] }->[0] :
        ($STATCACHE{ $_[0] } =
            ( -r $_[0] ?
                [ ( stat(_) )[ 9, 7, 10 ] ] :
                [ 0, 0, 0 ] )
        )->[0]
    );
}

sub cachedmtime_size {
    return (
        exists $STATCACHE{ $_[0] } ?
        @{ $STATCACHE{ $_[0] } }[ 0, 1 ] :
        @{ ( $STATCACHE{ $_[0] } =
            ( -r $_[0] ?
                [ ( stat(_) )[ 9, 7, 10 ] ] :
                [ 0, 0, 0 ] )
        ) }[ 0, 1 ]
    );
}

sub cachedmtime_ctime {
    return (
        exists $STATCACHE{ $_[0] } ?
        @{ $STATCACHE{ $_[0] } }[ 0, 2 ] :
        @{ ( $STATCACHE{ $_[0] } =
            ( -r $_[0] ?
                [ ( stat(_) )[ 9, 7, 10 ] ] :
                [ 0, 0, 0 ] ) )
         }[ 0, 2 ]
    );
}

sub clearcache {
    %STATCACHE = ();
    return 1;
}

sub api2 {
    return {
        'func' => 'clearcache',
    };
}

1;

} # --- END Cpanel/StatCache.pm


{ # --- BEGIN Cpanel/App.pm

package Cpanel::App;

our $appname = 'cpanel';

1;

} # --- END Cpanel/App.pm


{ # --- BEGIN Cpanel/ProcessCheck.pm
package Cpanel::ProcessCheck;


use strict;
use vars qw( @ISA @EXPORT $VERSION );

require Exporter;

@ISA    = qw( Exporter );
@EXPORT = qw( previouspids killdeadlock );

$VERSION = '0.1';

sub killdeadlock {
    my %AGS 	 = @_;
    my $process  = $AGS{process}    || return;
    my $arg      = $AGS{processarg} || 0;
    my $waittime = $AGS{timeout}    || 6;

    if ($waittime <= 5) { $waittime = 6; }

    my $pidcount = 1;
    my $printmessage = 1;
    my $lc = 0;
    while ($pidcount > 0) {
        $lc++;
        my(%PPIDS) = previouspids('process'    => $process,
                                  'processarg' => $arg);
        $pidcount = scalar keys %PPIDS;

        return if ($pidcount == 0);
        last if ( $lc >= ($waittime / 5) );
        if ( $printmessage ) {
            print "Waiting for $process lock release ...";
            $printmessage = 0;
        } else {
            print '...';
        }
        sleep(5);
    }
    print " Done\n";

    my %PPIDS = previouspids('process'    => $process,
                             'processarg' => $arg);
    foreach my $pid (sort keys %PPIDS) {
        if ($pid > 1) {
            print "Releasing $process lock with pid $pid ... ";
            my $numkilled = kill 9, $pid;
            if ( $numkilled != 1 ) {
               print "failed to kill process.\n";
            } else {
               print "Done\n";
            }
        }
    }
}

sub previouspids {
    my %AGS        = @_;
    my $search     = $AGS{process}    || return;
    my $searchtune = $AGS{processarg} || 0;

    my $parent_pid = getppid();
    my $isrunning = `ps uxawwwwwww`;

    my @PS = split(/\n/, $isrunning);
    @PS = grep(/$search/,@PS);
    return if ( $#PS == -1 );
    if ( $searchtune ) {
        @PS = grep(/$searchtune/,@PS);
    }
    @PS = grep(!/(?:defunct|zombie|\/rc\.d\/|\/init\.d\/)/,@PS);
    my %PIDS;
    foreach my $ps (@PS) {
        $ps =~ /\D+(\d+)/;
        my $pid = $1;
        $PIDS{$pid} = 1;
    }
    delete $PIDS{$parent_pid};
    delete $PIDS{$$};
     
    return %PIDS;
}

1;

} # --- END Cpanel/ProcessCheck.pm


{ # --- BEGIN Cpanel/SafeRun/Errors.pm
package Cpanel::SafeRun::Errors;


# use Cpanel::SafeRun::Simple ();


sub saferunallerrors {
    my $output_ref = Cpanel::SafeRun::Simple::_saferun_r( \@_, 1 );    #1 = errors to stdout
    return wantarray ? split( /\n/, $$output_ref ) : $$output_ref;
}

sub saferunnoerror {
    my $output_ref = Cpanel::SafeRun::Simple::_saferun_r( \@_, 2 );    # 2 = errors to devnull
    return wantarray ? split( /\n/, $$output_ref ) : $$output_ref;
}

1;

} # --- END Cpanel/SafeRun/Errors.pm


{ # --- BEGIN Cpanel/SafeRun/Simple.pm
package Cpanel::SafeRun::Simple;


use strict;

sub saferun_r {
	return _saferun_r(\@_);
}

sub _saferun_r {
    my ($cmdline, $error_flag) = @_;
    
    my $output;

    if ( substr( $cmdline->[0], 0, 1 ) eq '/' ) {
        my ($check) = !-e $cmdline->[0] && $cmdline->[0] =~ /[\s<>&\|\;]/ ? split(/[\s<>&\|\;]/, $cmdline->[0], 2) : $cmdline->[0]; 
        if (!-x $check) {
            $? = -1;
            return \$output;
        }
    };

    local ( $/ );
    my ($pid,$prog_fh);
    if ( $pid = open($prog_fh,'-|') ) {
        
    } else {
        ($ENV{'PATH'}) = $ENV{'PATH'} =~ m/(.*)/; # untaint, case 6622
	if ($error_flag && $error_flag == 1) { open(STDERR,'>&STDOUT'); }
	elsif ($error_flag && $error_flag == 2) { open(STDERR,'>','/dev/null'); }
        exec(@$cmdline);
        exit(127);
    };
    if (!$prog_fh || !$pid) {
        $? = -1;
        return \$output;
    }
    $output = readline($prog_fh);
    close($prog_fh);
    return \$output;
}

sub saferun {
    my $ref = _saferun_r(\@_,0);
    if ($ref) {
        return $$ref;
    }
    else {
        return;
    }
}

1;

} # --- END Cpanel/SafeRun/Simple.pm


{ # --- BEGIN Cpanel/Update/Logger.pm
package Cpanel::Update::Logger;


use strict;
use warnings;
use Cpanel::SafeDir::MK;
use File::Basename;

use constant {
    DEBUG => int 0,
    INFO  => int 25,
    WARN  => int 50,
    ERROR => int 75,
    FATAL => int 100,
};

our $VERSION = '1.0';

sub new {
    my $class = shift;
    my $self = shift || {};
    ref($self) eq 'HASH' or die("hashref not passed to new");

    bless( $self, $class );

    $self->{'stdout'} = 1 if ( !defined $self->{'stdout'} );

    eval { $self->set_logging_level( $self->{'log_level'} ); 1 }
      or die("An invalid logging level was passed to new: $self->{'log_level'}");

    $self->open_log if ( $self->{'logfile'} );

    if ( exists $self->{'pbar'} and defined $self->{'pbar'} ) {
        $self->{'pbar'} += 0;
        $self->update_pbar( $self->{'pbar'} );
    }

    return $self;
}

sub open_log {
    my $self = shift or die;

    my $log_file    = $self->{'logfile'};
    my $logfile_dir = File::Basename::dirname($log_file);
    my $created_dir = 0;
    if ( !-d $logfile_dir ) {
        Cpanel::SafeDir::MK::safemkdir( $logfile_dir, '0755', 2 );
        $created_dir = 1;
    }

    open( my $fh, '>>', $log_file ) or die("Cannot open '$self->{logfile}' for append.");
    my $orig_fh = select($fh);
    $| = 1;
    select($orig_fh);

    $self->{'fh'} = $fh;

    print {$fh} '-' x 100 . "\n";
    print {$fh} "=> Log opened from $0 at " . localtime(time) . "\n";
    $self->warn("Had to create directory $logfile_dir before opening log") if ($created_dir);
}

sub close_log {
    my $self = shift or die;

    my $fh = $self->{'fh'};
    print {$fh} "=> Log closed " . localtime(time) . "\n";

    warn("Failed to close file handle for $self->{'logfile'}") if ( !close $fh );
    delete $self->{'fh'};

    return;
}

sub DESTROY {
    my $self = shift or die("DESTROY called without an object");
    $self->close_log if ( $self->{'fh'} );
}

sub log {
    my $self = shift or die("log called as a class");
    ref $self eq __PACKAGE__ or die("log called as a class");

    my $msg = shift or return;

    my $stdout = shift;
    $stdout = $self->{'stdout'} if ( !defined $stdout );

    my $fh = $self->{'fh'};
    foreach my $line ( split /[\r\n]+/, $msg ) {
        my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime;
        my $time_stamp = sprintf( "%04d%02d%02d.%02d%02d%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec );

        chomp $line;
        print STDOUT "[$time_stamp] $line\n" if $stdout;
        print {$fh} "[$time_stamp] $line\n" if $fh;
    }
}

sub fatal {
    my $self = shift or die;
    return if ( $self->{'log_level_numeric'} > FATAL );

    $self->log("***** FATAL: $_[0]");
}

sub error {
    my $self = shift or die;
    return if ( $self->{'log_level_numeric'} > ERROR );

    $self->log("E $_[0]");
}

sub warning {
    my $self = shift or die;
    return if ( $self->{'log_level_numeric'} > WARN );

    $self->log("W $_[0]");
}

sub info {
    my $self = shift or die;
    return if ( $self->{'log_level_numeric'} > INFO );

    $self->log("  $_[0]");
}

sub debug {
    my $self = shift or die;
    return if ( $self->{'log_level_numeric'} > DEBUG );

    $self->log("D $_[0]");
}

sub set_logging_level {
    my $self = shift or die;

    my $log_level = shift;
    $log_level = 'info' if ( !defined $log_level );

    if ( $log_level =~ m/^fatal/i ) {
        $self->{'log_level'}         = 'fatal';
        $self->{'log_level_numeric'} = FATAL;
    }
    elsif ( $log_level =~ m/^error/i ) {
        $self->{'log_level'}         = 'error';
        $self->{'log_level_numeric'} = ERROR;
    }
    elsif ( $log_level =~ m/^warn/i ) {
        $self->{'log_level'}         = 'warning';
        $self->{'log_level_numeric'} = WARN;
    }
    elsif ( $log_level =~ m/^info/i ) {
        $self->{'log_level'}         = 'info';
        $self->{'log_level_numeric'} = INFO;
    }
    elsif ( $log_level =~ m/^debug/i ) {
        $self->{'log_level'}         = 'debug';
        $self->{'log_level_numeric'} = DEBUG;
    }
    else {
        die("Unknown logging level '$log_level' passed to set_logging_level");
    }
}

sub get_pbar { shift->{'pbar'} }

sub increment_pbar {
    my $self = shift or die;
    return if ( !exists $self->{'pbar'} );

    my $amount = shift || 1;
    my $new_value = $self->{'pbar'} + $amount;

    $self->update_pbar($new_value);
}

sub update_pbar {
    my $self = shift or die;
    return if ( !exists $self->{'pbar'} );

    my $new_value = shift || 0;
    if ( $new_value > 100 ) {
        $self->debug("Pbar set to > 100 ($new_value)");
        $new_value = 100;
    }

    $self->{'pbar'} = $new_value;

    $self->info( $new_value . '% complete' );
}

1;

} # --- END Cpanel/Update/Logger.pm


{ # --- BEGIN Cpanel/SafeDir/MK.pm

package Cpanel::SafeDir::MK;

# use Cpanel::Logger ();

sub safemkdir {
    my $dir    = shift;
    my $mode   = shift;
    my $errors = shift;

    if ( defined $mode && $mode ne '' ) {
        if ( $mode !~ /^[0-7]{3,4}$/ ) {
            $mode = '0755';
        }
        elsif ( $mode =~ /^[0-7]{3}$/ ) {
            $mode = '0' . $mode;
        }
    }

    my $default = '';
    if ( $dir =~ m/^\// ) {
        $default = '/';
    }
    elsif ( $dir eq '.' || $dir eq './' ) {
        if ( !-l $dir && $mode ) {
            return chmod oct($mode), $dir;
        }
        return 1;
    }
    else {
        $dir =~ s/^\.\///;
    }

    if ( $dir =~ m/(?:^|\/)\.\.(?:\/|$)/ || $dir =~ m/\/{2}/ ) {
        Cpanel::Logger::logger(
            {
                'message' => "Possible improper directory $dir specified",
                'level'   => 'warn',
                'service' => __PACKAGE__,
                'output'  => $errors,
                'backtrace' => 0,
            }
        );
        my @dir_parts = split /\//, $dir;
        my @good_parts;
        my $first;
        foreach my $part (@dir_parts) {
            next if ( !defined $part || $part eq '' );
            next if $part eq '.';
            if ( $part eq '..' ) {
                if ( !$first || !@good_parts ) {
                    Cpanel::Logger::logger(
                        {
                            'message' =>
"Will not proceed above first directory part $first",
                            'level'   => 'warn',
                            'service' => __PACKAGE__,
                            'output'  => $errors,
                            'backtrace' => 0,
                        }
                    );
                    return 0;
                }
                if ( $first eq $good_parts[$#good_parts] ) {
                    undef $first;
                }
                pop @good_parts;
                next;
            }
            elsif ( $part =~ m/^\.+$/ ) {
                Cpanel::Logger::logger(
                    {
                        'message' => "Total stupidity found in directory $dir",
                        'level'   => 'warn',
                        'service' => __PACKAGE__,
                        'output'  => $errors,
                        'backtrace' => 0,
                    }
                );
                return 0;
            }
            push @good_parts, $part;
            if ( !$first ) { $first = $part }
        }
        $dir = $default . join '/', @good_parts;
        if ( !$dir ) {
            Cpanel::Logger::logger(
                {
                    'message' => "Could not validate given directory",
                    'level'   => 'warn',
                    'service' => __PACKAGE__,
                    'output'  => $errors,
                    'backtrace' => 0,
                }
            );
            return;
        }
        Cpanel::Logger::logger(
            {
                'message' => "Improper directory updated to $dir",
                'level'   => 'warn',
                'service' => __PACKAGE__,
                'output'  => $errors
            }
        );
    }

    if ( -d $dir ) {
        if ( !-l $dir && $mode ) {
            return chmod oct($mode), $dir;
        }
        return 1;
    }
    elsif ( -e _ ) {
        Cpanel::Logger::logger(
            {
                'message' => "$dir was expected to be a directory!",
                'level'   => 'warn',
                'service' => __PACKAGE__,
                'output'  => $errors
            }
        );
        return 0;
    }

    my @dir_parts = split /\//, $dir;

    if ( scalar @dir_parts > 100 ) {
        Cpanel::Logger::logger(
            {
                'message' =>
"Encountered excessive directory length. This should never happen.",
                'level'   => 'warn',
                'service' => __PACKAGE__,
                'output'  => $errors
            }
        );
        return 0;
    }
    my $returnvalue;
    foreach my $i ( 0 .. $#dir_parts ) {
        my $newdir = join( '/', @dir_parts[ 0 .. $i ] );
        next if $newdir eq '';
        my $is_dir = -d $newdir;
        my $exists = -e _;

        if ( !$exists ) {
            my $local_mode = $mode ? $mode : '0755';
            if ( mkdir( $newdir, oct($local_mode) ) ) {
                $returnvalue++;
            }
            else {
                Cpanel::Logger::logger(
                    {
                        'message' => "mkdir $newdir failed: $!",
                        'level'   => 'warn',
                        'service' => __PACKAGE__,
                        'output'  => $errors
                    }
                );
                return;
            }
        }
        elsif ( !$is_dir ) {
            Cpanel::Logger::logger(
                {
                    'message' =>
                      "Encountered non-directory $newdir in path of $dir: $!",
                    'level'   => 'warn',
                    'service' => __PACKAGE__,
                    'output'  => $errors
                }
            );
            last;
        }
    }
    return $returnvalue;
}

sub safemkdir_as_user {
    my ($user, $path, $mode) = @_;

    if ( $> == 0 ) {
        require Cpanel::AccessIds;
        return Cpanel::AccessIds::do_as_user( $user,
            sub {
                $path =~ /^(.*)$/;
                $path = $1;
                return Cpanel::SafeDir::MK::safemkdir( $path, $mode );
            }
        );
    }
    else {
        return Cpanel::SafeDir::MK::safemkdir( $path, $mode );
    }
}

1;

} # --- END Cpanel/SafeDir/MK.pm


{ # --- BEGIN Cpanel/Logger.pm
package Cpanel::Logger;


# use Cpanel::Time    ();

my $tree;
our $VERSION = 1.0;

my $std_log_file = '/usr/local/cpanel/logs/error_log';

my ( $cached_progname, $cached_prog_pid, %singleton_stash );

sub new {
    my ( $class, $hr_args ) = @_;
    
    my $args_sig = 'no_args';
    if ( $hr_args && ref($hr_args) eq 'HASH' ) {

        $args_sig = join( ',', map { $_ . '=>' . $hr_args->{$_} } sort keys %{$hr_args} );    # Storable::freeze($hr_args);
    }

    if ( exists $singleton_stash{$class}{$args_sig} ) {
        $singleton_stash{$class}{$args_sig}->{'cloned'}++;
    }
    else {
        $singleton_stash{$class}{$args_sig} = bless( {}, $class );
        if ( $hr_args && ref($hr_args) eq 'HASH' ) {
            foreach my $k ( keys %$hr_args ) {
                $singleton_stash{$class}{$args_sig}->{$k} = $hr_args->{$k};
            }
        }
    }

    if ( !$singleton_stash{$class}{$args_sig}->{'cloned'} ) {
        $singleton_stash{$class}{$args_sig}->reset_accumulator();
        $singleton_stash{$class}{$args_sig}->accumulate(1);
    }

    if ( exists $ENV{'CPANEL_LOGGER_FILE'} && $ENV{'CPANEL_LOGGER_FILE'} ) {
        my $alt_log_file = $ENV{'CPANEL_LOGGER_FILE'};
        $alt_log_file =~ s/\.\.//g; # Prevent directory traversal
        if ( $> == 0 ) {
            if ( $alt_log_file =~ m/([^\/]+)$/ ) {
                $alt_log_file = $1;
            }
            $std_log_file = '/usr/local/cpanel/logs/' . $alt_log_file;
        }
        else {
            $std_log_file = $alt_log_file;
        }
    }

    return $singleton_stash{$class}{$args_sig};
}

sub __Logger_pushback {
    my (@list) = @_;
    my $p = __PACKAGE__;
    if ( @list && ref( $list[0] ) =~ /^${p}/ ) {
        return ( shift(@list), @list );
    }
    else {
        my $self = $p->new();
        return ( $self, @list );
    }
}

sub accumulate {
    my ( $self, $on ) = &__Logger_pushback(@_);
    $self->{'accumulator_on'} = ( $on || !defined($on) ? 1 : 0 );
}

sub no_accumulate {
    &accumulate( @_, 0 );
}

sub is_accumulated {
    my ($self) = &__Logger_pushback(@_);
    return ( @{ $self->{'container'} } ? 1 : 0 );
}

sub get_accumulate {
    my ($self) = &__Logger_pushback(@_);
    return wantarray ? @{ $self->{'container'} } : $self->{'container'};
}

sub get_messages {
    &get_accumulate(@_);
}

sub get_last_msg {
    my ($self) = &__Logger_pushback(@_);
    return ${ $self->{'container'} }[ $#{ $self->{'container'} } ];
}    # end of get_last_msg

sub get_msg_of_type {
    my ( $self, $type ) = &__Logger_pushback(@_);
    return if !$type;
    my @msgs;
    foreach my $m ( @{ $self->{'container'} } ) {
        push( @msgs, $m ) if ( $m =~ m/^$type/ );
    }
    return wantarray ? @msgs : \@msgs;
}    # end of get_msg_of_type

sub clear_messages {
    &reset_accumulator(@_);
}

sub reset_accumulator {
    my ($self) = &__Logger_pushback(@_);
    $self->{'container'} = [];
}

sub store_message {
    my ( $self, @list ) = &__Logger_pushback(@_);
    if ( $self->{'accumulator_on'} ) {

        while ( $#list > 2 ) {
            pop(@list);
        }

        push( @{ $self->{'container'} }, sprintf( "%s [%s] %s", @list ) );
    }
}

sub invalid {
    my ( $self, @list ) = &__Logger_pushback(@_);

    my %log  = (
        'message'   => $list[0],
        'level'     => 'invalid',
        'service'   => $self->find_progname(),
        'output'    => 0,
        'backtrace' => 1,
        'die'       => 0,
    );

    if ( -e '/var/cpanel/dev_sandbox' ) {
        if ( !-e '/var/cpanel/DEBUG' ) {
            require Cpanel::SafeRun::Errors;

            my $time = Cpanel::Time::localtime2timestamp();
            Cpanel::SafeRun::Errors::saferunallerrors( '/usr/local/cpanel/scripts/icontact', '--define', qq[app=$log{'service'}], '--define', qq[subject=Cpanel::Logger::invalid called in $log{'service'}], '--define', "message=$time [$log{'service'}] " . $self->backtrace( $log{'message'} ), '--define', q{level=3}, );
        }
        $log{'output'} = -t STDIN ? 2 : 1;
    }
    $self->logger( \%log );
}    # end of invalid

sub debug {
    my ( $self, $message, $conf_hr ) = @_;    # not appropriate for debug() : __Logger_pushback(@_);

    $self = $self->new() if !ref $self;

    $conf_hr ||= {
        'force'     => 0,
        'backtrace' => 0,
        'output'    => 0,
    };
    return unless $conf_hr->{'force'} || ( defined $Cpanel::Debug::level && $Cpanel::Debug::level );

    if ( !defined $message ) {
        my @caller = caller();
        $message = "debug() at $caller[1] line $caller[2].";
    }

    my %log = (
        'message'   => $message,
        'level'     => 'debug',
        'service'   => $self->find_progname(),
        'output'    => $conf_hr->{'output'},
        'backtrace' => $conf_hr->{'backtrace'},
    );

    if ( ref $log{'message'} ) {

        my $outmsg = $log{'message'};
        eval 'local $SIG{__DIE__}; local $SIG{__WARN__}; require YAML::Syck; $outmsg = YAML::Syck::Dump($outmsg) ';
        my @caller = caller();
        $log{'message'} = "$log{'message'} at $caller[1] line $caller[2]:\n" . $outmsg;
    }
    elsif ( $log{'message'} =~ m/\A\d+(?:\.\d+)?\z/ ) {
        $log{'message'} = "debug() number $log{'message'}";
    }

    $self->store_message( $log{'level'}, $log{'service'}, $log{'message'} );
    $self->logger( \%log );

    return \%log;
}

sub info {
    my ( $self, @list ) = &__Logger_pushback(@_);
    my %log = (
        'message'   => $list[0],
        'level'     => 'info',
        'service'   => $self->find_progname(),
        'output'    => 0,
        'backtrace' => 0
    );
    $self->store_message( $log{'level'}, $log{'service'}, $log{'message'} );
    $self->logger( \%log );
}    # end of info

sub warn {
    my ( $self, @list ) = &__Logger_pushback(@_);
    my %log = (
        'message'   => $list[0],
        'level'     => 'warn',
        'service'   => $self->find_progname(),
        'output'    => -t STDIN ? 2 : 0,
        'backtrace' => 1
    );
    $self->store_message( $log{'level'}, $log{'service'}, $log{'message'} );
    $self->logger( \%log );
}    # end of warn

sub die {
    my ( $self, @list ) = &__Logger_pushback(@_);
    my %log = (
        'message'   => $list[0],
        'level'     => 'die',
        'service'   => $self->find_progname(),
        'output'    => -t STDIN ? 2 : 0,
        'backtrace' => 1
    );
    $self->store_message( $log{'level'}, $log{'service'}, $log{'message'} );
    $self->logger( \%log );
}    # end of die

sub panic {
    my ( $self, @list ) = &__Logger_pushback(@_);
    my %log = (
        'message'   => $list[0],
        'level'     => 'panic',
        'service'   => $self->find_progname(),
        'output'    => 2,
        'backtrace' => 1
    );
    $self->store_message( $log{'level'}, $log{'service'}, $log{'message'} );
    $self->logger( \%log );
}    # end of panic

sub cplog {
    my $msg      = shift;
    my $loglevel = shift;
    my $service  = shift;
    my $nostdout = shift;
    if ( !$nostdout ) {
        $nostdout = 1;
    }
    else {
        $nostdout = 0;
    }
    logger( { 'message' => $msg, 'level' => $loglevel, 'service' => $service, 'output' => $nostdout, 'backtrace' => 1 } );
}    # end of cplog (deprecated)

sub event {
    my ( $self, @list ) = &__Logger_pushback(@_);
    my %args;
    if ( @list && @list % 2 ) {
        $args{'title'} = $list[0];
        return if !$args{'title'};
    }
    else {
        %args = @list;
    }

    if ( !$args{'summary'} ) {
        $args{'summary'} = '';
    }

    $self->logger(
        {
            'message'   => $args{'title'},
            'service'   => $args{'summary'},
            'output'    => 0,
            'backtrace' => 1,
        }
    );
}

sub logger {
    my ( $self, @list ) = &__Logger_pushback(@_);
    my $hr = ref( $list[0] ) eq 'HASH' ? $list[0] : { 'message' => $list[0] };
    $hr->{'message'} ||= 'Something is wrong';

    $hr->{'level'}   ||= '';
    $hr->{'service'} ||= $self->find_progname();
    $hr->{'output'}  ||= 0;
    if ( !exists $hr->{'backtrace'} ) {
        $hr->{'backtrace'} = 1;
    }

    my $time = Cpanel::Time::localtime2timestamp();
    my ( $usingstderr, $logfile ) = ( 0, ( $self->{'alternate_logfile'} ? $self->{'alternate_logfile'} : $std_log_file ) );
    my $log_fh;
    unless ( open $log_fh, '>>', $logfile ) {
        ( $usingstderr, $log_fh ) = ( 1, \*STDERR );
    }
    syswrite( $log_fh, '[' . $time . '] ' . $hr->{'level'} . ' [' . $hr->{'service'} . '] ' . ( $hr->{'backtrace'} ? $self->backtrace( $hr->{'message'} ) : $hr->{'message'} . "\n" ) );

    if ( $hr->{'level'} eq 'panic' || $hr->{'level'} eq 'invalid' ) {
        if ( open my $panic_fh, '>>', '/usr/local/cpanel/logs/panic_log' ) {
            syswrite( $panic_fh, "$time $hr->{level} [$hr->{'service'}] " . ( $hr->{'backtrace'} ? $self->backtrace( $hr->{'message'} ) : $hr->{'message'} . "\n" ) );
            close $panic_fh;
        }
    }
    else {

        $hr->{'output'} = 1;
    }

    if ( $hr->{'output'} ) {
        my $out = "$hr->{level} [$hr->{'service'}] $hr->{'message'}\n";

        if ( $hr->{'output'} == 3 ) {
            print STDOUT $out;
            print STDERR $out;
        }
        elsif ( $hr->{'output'} == 1 && -t STDOUT ) {
            print STDOUT $out;
        }
        elsif ( $hr->{'output'} == 2 ) {
            print STDERR $out;
        }
    }

    if ( !$usingstderr ) {
        close $log_fh;
    }
    if ( ( $hr->{'level'} eq 'die' || $hr->{'level'} eq 'panic' || $hr->{'die'} ) ) {
        CORE::die "exit level [$hr->{'level'}] ($hr->{'message'})\n";
    }
}    # end of logger

sub find_progname {
    if ( $cached_progname && $cached_prog_pid == $$ ) {
        return $cached_progname;
    }
    my $s;
    my $i = 1;    # 0 is always find_progname
    while ( my @service = caller( $i++ ) ) {
        last if ( $service[3] =~ /::BEGIN$/ );
        $s = $service[1] if ( $service[1] ne '' );
    }

    $s =~ s@.+/(.+)$@$1@;

    $s =~ s@\..+$@@;

    $cached_progname = $s;
    $cached_prog_pid = $$;

    $s;
}

sub backtrace {
    my ( $self, @list ) = &__Logger_pushback(@_);
    return (@list) if ( ref $list[0] );
    local $_;    # Protect surrounding program - just in case...
    my ( $pack, $file, $line, $sub, $hargs, $eval, $require, @parms );
    my $error = join( '', @list );
    my $msg   = '';
    my $i     = 0;
    while (
        do {
            {

                package DB;
                ( $pack, $file, $line, $sub, $hargs, undef, $eval, $require ) = caller( $i++ )
            }
        }
      ) {
        next if ( $pack eq 'Cpanel::Logger' );
        if ( $error eq '' ) {
            if ( defined $eval ) {
                $eval =~ s/([\\\'])/\\$1/g unless ($require);                      # Escape \ and '
                $eval =~ s/([\x00-\x1F\x7F-\xFF])/sprintf("\\x%02X",ord($1))/eg;
                if   ($require) { $sub = "require $eval"; }
                else            { $sub = "eval '$eval'"; }
            }
            elsif ( $sub eq '(eval)' ) { $sub = 'eval {...}'; }
            else {
                @parms = ();
                if ($hargs) {

                    @parms = @DB::args;
                    for (@parms) {
                        if ( defined $_ ) {
                            if ( ref $_ ) {
                                $_ = "$_";
                            }
                            else {

                                unless (/^-?\d+(?:\.\d+(?:[eE][+-]\d+)?)?$/) {

                                    s/([\\\'])/\\$1/g;
                                    s/([\x00-\x1F\x7F-\xFF])/sprintf("\\x%02X",ord($1))/eg;
                                    $_ = "'$_'";
                                }
                            }
                        }
                        else { $_ = 'undef'; }
                    }
                }
                $sub .= '(' . join( ', ', @parms ) . ')';
            }
            if ( $msg eq '' ) { $msg = "\n\t$sub called"; }
            else              { $msg .= "\t$sub called"; }
        }
        else {
            $msg = $error;
        }

        $msg .= " at $file line $line\n";
        $error = '';
    }
    $msg ||= $error;
    $msg =~ tr/\0//d;    # Circumvent die's incorrect handling of NUL characters
    $msg;
}    # end of backtrace

1;


} # --- END Cpanel/Logger.pm


{ # --- BEGIN Cpanel/Time.pm
package Cpanel::Time;



my ( $timecacheref, $localtimecacheref ) = ( [ -1, '', -1 ], [ -1, '', -1 ] );
my @weekday = qw/Sun Mon Tue Wed Thu Fri Sat/;
my @longday = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
my @month   = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;

sub time2http {
    my $time = shift || time();
    my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime $time;
    return sprintf( '%s, %02d %s %04d %02d:%02d:%02d GMT', $weekday[$wday], $mday, $month[$mon], $year + 1900, $hour, $min, $sec );
}

sub time2clftime {
    my $time = shift || time();

    my ( $sec, $min, $hour, $mday, $mon, $year ) = gmtime $time;
    return sprintf( '%02d/%02d/%04d:%02d:%02d:%02d -0000', $mon + 1, $mday, $year + 1900, $hour, $min, $sec );
}

sub time2dnstime {
    my $time = shift || time();
    my ( $sec, $min, $hour, $mday, $mon, $year ) = gmtime $time;
    return sprintf( '%04d%02d%02d', $year + 1900, $mon + 1, $mday );
}

sub time2condensedtime {
    my $time = shift || time();
    my ( $sec, $min, $hour, $mday, $mon, $year ) = gmtime $time;
    return sprintf( '%04d%02d%02d%02d%02d%02d', $year + 1900, $mon + 1, $mday, $hour, $min, $sec );
}

sub time2datetime {
    my $time = shift || time();
    my $delimiter = shift;
    $delimiter = ' ' unless defined $delimiter;

    return $timecacheref->[2] if $timecacheref->[0] == $time && $timecacheref->[1] eq $delimiter;

    @{$timecacheref}[ 0, 1 ] = ( $time, $delimiter );
    my ( $sec, $min, $hour, $mday, $mon, $year ) = gmtime $time;
    return sprintf( '%04d-%02d-%02d' . $delimiter . '%02d:%02d:%02d', $year + 1900, $mon + 1, $mday, $hour, $min, $sec );
}

sub localtime2timestamp {
    my $time = shift || time();
    my $delimiter = shift;
    $delimiter = ' ' unless defined $delimiter;

    return $localtimecacheref->[2] if $localtimecacheref->[0] == $time && $localtimecacheref->[1] eq $delimiter;

    my $tz_offset = get_server_offset_as_offset_string();

    @{$localtimecacheref}[ 0, 1 ] = ( $time, $delimiter );
    my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime $time;
    return ( $localtimecacheref->[1] = sprintf( '%04d-%02d-%02d' . $delimiter . '%02d:%02d:%02d %s', $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $tz_offset ) );
}

sub time2cookie {
    my $time = shift || time();
    my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime $time;
    return sprintf( '%s, %02d-%s-%02d %02d:%02d:%02d GMT', $longday[$wday], $mday, $month[$mon], $year % 100, $hour, $min, $sec );
}

my $server_offset;

sub get_server_offset_in_seconds {
    if ( !defined $server_offset ) {
        if ( get_server_offset_as_offset_string() =~ m/([-+]?[0-9]{2})([0-9]{2})/ ) {
            my ( $hours, $minutes ) = ( $1, $2 );
            my $seconds = ( ( abs($hours) * 60 * 60 ) + ( $minutes * 60 ) );
            $server_offset = $hours < 0 ? "-$seconds" : $seconds;
        }
        else {

            $server_offset = 0;
        }
    }
    return $server_offset;
}

my $server_offset_string;

sub get_server_offset_as_offset_string {
    if ( !defined $server_offset_string ) {
        my $time = time;
        my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime $time;

        my ( $gmmin, $gmhour, $gmday ) = ( gmtime($time) )[ 1, 2, 3 ];

        my $gmoffset = ( $hour * 60 + $min ) - ( $gmhour * 60 + $gmmin ) + 1440 * ( $mday <=> $gmday );
        $server_offset_string = sprintf( '%+03d%02d', int( $gmoffset / 60 ), $gmoffset % 60 );
    }
    return $server_offset_string;
}

1;

} # --- END Cpanel/Time.pm


{ # --- BEGIN Cpanel/AccessIds.pm
package Cpanel::AccessIds;


use strict;
# use Cpanel::PwCache            ();
# use Cpanel::Logger             ();
# use Cpanel::AccessIds::SetUids ();

our $VERSION = '1.1';

my $logger;

{
    no warnings 'once';
    *setuid            = \&Cpanel::AccessIds::SetUids::setuids;
    *setuids           = \&Cpanel::AccessIds::SetUids::setuids;
    *run_as_user_group = \&runasusergroup;
    *run_as_user       = \&runasuser;
}

sub runasuser {
    my $user = shift;
    my $gid  = ( Cpanel::PwCache::getpwnam($user) )[3];    # gets passed to setuids() which works w/ numeric id or name
    my @CMDS = @_;
    return runasusergroup( $user, $gid, @CMDS );
}

sub runasusergroup {
    my ( $user, $group, @CMDS ) = @_;
    my $homedir = '';
    if ( $user !~ m/^\d+$/ ) {
        $homedir = ( Cpanel::PwCache::getpwnam($user) )[7];
    }
    else {
        $homedir = ( Cpanel::PwCache::getpwuid($user) )[7];
    }

    if ( my $pid = fork() ) {
        waitpid( $pid, 0 );
    }
    else {
        Cpanel::AccessIds::SetUids::setuids( $user, $group );
        $ENV{'HOME'} = $homedir;

        exec @CMDS;
        exit 1;
    }
    return;
}

sub do_as_user {
    my $user = shift;
    my $code = shift;

    my $gid = ( Cpanel::PwCache::getpwnam($user) )[3];
    return do_as_user_group( $user, $gid, $code, @_ );
}

sub do_as_user_group {
    my $user = shift;
    return if !$user;
    my $group = shift;
    return if !$group;
    my $code = shift;
    if ( !$code || ref $code ne 'CODE' ) {
        $logger ||= Cpanel::Logger->new();
        $logger->warn("Failed to provide CODE");
        return;
    }

    require Storable;
    my ( $PR, $CW, $CR, $PW, $RC );
    pipe( $PR, $CW );
    pipe( $CR, $PW );

    if ( defined &IO::Handle::autoflush ) {
        $CW->autoflush();
        $PW->autoflush();
    }

    if ( my $pid = fork() ) {
        $RC = Storable::fd_retrieve($CR);
        close $CR;
        close $CW;
        waitpid( $pid, 0 );
    }
    else {

        Cpanel::AccessIds::SetUids::setuids( $user, $group );
        Storable::nstore_fd( [ $code->(@_) ], $PW );
        close $PR;
        close $PW;
        exit 0;
    }

    return @{$RC};
}

sub loadfile_as_user {
    my $user = shift;
    my $file = shift;

    my ( $uid, $gid ) = ( Cpanel::PwCache::getpwnam($user) )[ 2, 3 ];

    if ( !defined $uid || !defined $gid ) {
        $logger ||= Cpanel::Logger->new();
        $logger->warn('Attempting to use non-existent user.');
        return;
    }

    my $data;
    if ( $< != 0 && $uid != $> ) {
        $logger ||= Cpanel::Logger->new();
        $logger->panic("Attempting to setuid as a normal user $>");
    }

    my $orig_euid = $>;
    my $orig_egid = $);


    my $change_effectives = $< == 0 && $uid != $> ? 1 : 0;

    if ($change_effectives) {
        $) = "$gid $gid";    # set EGID before EUID,
        $> = $uid;
    }

    if ( open my $rr_fh, '<', $file ) {
        local $/;
        $data = readline($rr_fh);
        close $rr_fh;
    }
    else {
        $logger ||= Cpanel::Logger->new();
        $logger->warn("Could not open file '$file' for reading: $!");
    }

    if ($change_effectives) {
        $> = $orig_euid;    # reset EUID before EGID
        local $! = 0;
        $) = $orig_egid
          ; # on some BSD's (others?) setgroups() behaves oddly (see case 33632). The result is you might lose one group. There is really no way to get it set to what it was originally, even some/most/all Linux distros do a reverse sort on the values so the order will change. These are limitations we can live with (vs. having all sorts of logic to try and address each possible nuance without gaurantee that it will have any effect) in this context since only root can get to this point and root can already read anything
        if ($!) {
            $logger ||= Cpanel::Logger->new();
            $logger->warn("Resetting the EGID ($)) to '$orig_egid' had some errors: $!");
        }
    }

    return $data;
}

1;

} # --- END Cpanel/AccessIds.pm


{ # --- BEGIN Cpanel/PwCache.pm
package Cpanel::PwCache;


use Carp             ();
# use Cpanel::SafeFile ();
# use Cpanel::Debug    ();
use IO::Handle       ();

$Cpanel::PwCache::VERSION = '3.1';

my ( $system, $isopen, $pwcacheistied, $pwcache_inited, $MIN_FIELDS_FOR_VALID_ENTRY, $pwcache_has_uid_cache, $pwcache_ref, $getpwent_open_time, $pwdatafuncuid, $pwdatafunc, $pwcachetie, $skip_uid_cache, $getpwent_passwd_fh, @SHAREDPW ) = ( $^O, 0, 0, 0, 6 );

our ( $CASE_SENSITIVE, $CASE_INSENSITIVE ) = ( 0, 1 );

sub istied       { $pwcacheistied }
sub deinit       { $pwcacheistied = 0; }
sub no_uid_cache { $skip_uid_cache = 1; }
sub uid_cache    { $skip_uid_cache = 0; }
sub pwcheckos    { return $system; }
*_getos = *pwcheckos;
sub init { ( $pwcachetie, $skip_uid_cache, $pwcacheistied ) = ( $_[0], $_[1], 1 ); }

sub validate {
    my ( $pwkey, $record ) = @_;
    if ( exists $record->{'contents'} && ref $record->{'contents'} ne 'ARRAY' && scalar @{ $record->{'contents'} } ) { return 0; }
    my ( $stored_mtime, $hstored_mtime ) = ( $record->{'cachetime'}, $record->{'hcachetime'} );

    my ( $passwdmtime, $hpasswdmtime ) = (
          ( stat('/etc/passwd') )[9], ( $system eq 'freebsd' && -r '/etc/master.passwd' )
        ? ( stat(_) )[9]
        : ( -r '/etc/shadow' ? ( stat(_) )[9] : 0 )
    );

    if ( $Cpanel::Debug::level > 3 ) {
        print STDERR __PACKAGE__ . "::validate called for record " . dump_rec($record) . "\n";
    }

    if (   $hstored_mtime
        && $stored_mtime
        && $hpasswdmtime == $hstored_mtime
        && $passwdmtime == $stored_mtime ) {
        if (  !$pwcache_ref->{$pwkey}->{'cachetime'}
            || $pwcache_ref->{$pwkey}->{'cachetime'} != $stored_mtime
            || $pwcache_ref->{$pwkey}->{'hcachetime'} != $hstored_mtime ) {
            @{ $pwcache_ref->{$pwkey} }{ 'hcachetime', 'cachetime', 'contents' } = ( $hstored_mtime, $stored_mtime, $record->{'contents'} );
        }
        if ( $Cpanel::Debug::level > 3 ) {
            print STDERR __PACKAGE__ . "::validate returned 1 " . dump_rec($record) . "\n";
        }
        return 1;

    }

    if ( $Cpanel::Debug::level > 3 ) {
        print STDERR __PACKAGE__ . "::validate returned 0 " . dump_rec($record) . "\n";
        print STDERR __PACKAGE__ . "::validate returned 0 [ hstored_mtime = $hstored_mtime, hpasswdmtime = $hpasswdmtime ] [ stored_mtime = $stored_mtime, passwdmtime = $stored_mtime ]\n";
    }

    return 0;
}

sub load {
    my $pwkey = shift;
    my ( $field, $value ) = split( /:/, $pwkey );

    my ( $passwdmtime, $hpasswdmtime ) = (
          ( stat('/etc/passwd') )[9], ( $system eq 'freebsd' && -r '/etc/master.passwd' )
        ? ( stat(_) )[9]
        : ( -r '/etc/shadow' ? ( stat(_) )[9] : 0 )
    );

    if ( $Cpanel::Debug::level > 3 ) {
        print STDERR __PACKAGE__ . "::load called for pwkey $pwkey\n";
    }

    my $pwdata = _getpwdata( $value, $field, $passwdmtime, $hpasswdmtime );

    @{ $pwcache_ref->{$pwkey} }{ 'hcachetime', 'cachetime', 'contents' } = ( $hpasswdmtime, $passwdmtime, $pwdata );

    return $pwcache_ref->{$pwkey};
}

sub pwmksafecache {
    foreach my $pwkey ( keys %{$pwcache_ref} ) {
        ${ $pwcache_ref->{$pwkey}->{'contents'} }[1] = 'x';
    }
}

sub getpwent {
    my ( $passwdmtime, $shadowmtime, $cryptpw ) = @_;

    if ( !$isopen ) {
        if ($getpwent_passwd_fh) { close($getpwent_passwd_fh); }
        $getpwent_passwd_fh = IO::Handle->new();
        open( $getpwent_passwd_fh, '<', '/etc/passwd' );
        if ( !$getpwent_passwd_fh ) {
            Carp::cluck "Unable to open /etc/passwd: $!";
            return;
        }
        $getpwent_open_time = ( stat('/etc/passwd') )[9];
        $shadowmtime = $passwdmtime = $getpwent_open_time;

        $isopen = 1;
    }
    elsif ( !$passwdmtime || !$shadowmtime ) {
        $shadowmtime = $passwdmtime = ( $getpwent_open_time ? $getpwent_open_time : ( stat('/etc/passwd') )[9] );

    }

    while ( my $passwd_line = readline($getpwent_passwd_fh) ) {
        chomp $passwd_line;
        @SHAREDPW = ( split( /:/, $passwd_line ), undef );
        if ( scalar @SHAREDPW > 7 && $SHAREDPW[0] =~ m/^[A-Za-z0-9_]/ ) {    # Check for valid lines and skip commented/invalid lines
            return wantarray ? ( $SHAREDPW[0], ( $cryptpw ? $cryptpw : $SHAREDPW[1] ), $SHAREDPW[2], $SHAREDPW[3], '', '', $SHAREDPW[4], $SHAREDPW[5], $SHAREDPW[6], -1, -1, $passwdmtime, $shadowmtime ) : $SHAREDPW[0];
        }
    }
    return;
}

sub getpwuid {

    my $pwref = Cpanel::PwCache::_pwfunc( $_[0], 2 );
    if ($pwref) {
        return wantarray ? @$pwref : $pwref->[0];
    }
    return;    #important not to return 0
}

sub getpwnam {

    my $pwref = Cpanel::PwCache::_pwfunc( $_[0], 0 );

    if ($pwref) {
        return wantarray ? @$pwref : $pwref->[2];
    }
    return;    #important not to return 0
}

sub pwclearcache {
    $pwcache_inited        = undef;
    $pwcache_has_uid_cache = undef;
    $pwcache_ref           = {};
}

sub setpwent {
    if ($getpwent_passwd_fh) { seek( $getpwent_passwd_fh, 0, 0 ); }
    if ( $getpwent_passwd_fh && eof($getpwent_passwd_fh) ) {
        $isopen = 0;
        if ($getpwent_passwd_fh) { close($getpwent_passwd_fh); }
    }
}

sub endpwent {
    if ($getpwent_passwd_fh) { close($getpwent_passwd_fh); }
    $isopen = 0;
}

sub _readshadow {
    my ( $value, $field, $passwdmtime, $shadowmtime ) = ( $_[0], ( $_[1] || 0 ), ( $_[2] || ( stat('/etc/passwd') )[9] ), ( $_[3] || ( stat('/etc/shadow') )[9] ) );
    my @PW = Cpanel::PwCache::_readpasswd( $value, $field, $passwdmtime, $shadowmtime );
    return if !@PW;

    my $match = quotemeta( $PW[0] ) . ':';
    if ( open my $shadow_fh, '<', '/etc/shadow' ) {

        while ( my $line = <$shadow_fh> ) {
            if ( $line =~ m/^$match/ ) {
                chomp $line;
                my @SH = split( /:/, $line );
                if ( @SH && defined( $SH[0] ) ) {
                    ( $PW[1], $PW[9], $PW[10], $PW[11], $PW[12] ) = (
                        $SH[1],    #encrypted pass
                        $SH[5],    #expire time
                        $SH[2],    #change time
                        $passwdmtime,
                        $shadowmtime
                    );
                    close $shadow_fh;
                    return @PW;
                }
            }
        }
        close $shadow_fh;
    }
    else {
        Carp::cluck("Unable to open /etc/shadow: $!");
    }
    Carp::cluck("Entry for $value missing in /etc/shadow");
    return @PW;
}

sub _readpasswd {
    my ( $value, $field, $passwdmtime, $shadowmtime, $pwline, @PW ) = ( $_[0], ( $_[1] || 0 ), ( $_[2] || ( stat('/etc/passwd') )[9] ), $_[3] );

    if ( open( my $passwd_fh, '<', '/etc/passwd' ) ) {

        if ( $field == 0 ) {
            my $match = quotemeta($value) . ':';
            while ( !eof($passwd_fh) ) {
                if ( !defined( $pwline = readline($passwd_fh) ) ) {

                    return;
                }
                if ( $pwline =~ m/^$match/ ) {
                    chomp $pwline;
                    @PW = split( /:/, $pwline );
                    close($passwd_fh);

                    return ( $PW[0], $PW[1], $PW[2], $PW[3], '', '', $PW[4], $PW[5], $PW[6], -1, -1, $passwdmtime, ( $shadowmtime || $passwdmtime ) );
                }
            }

        }
        else {

            while ( !eof($passwd_fh) ) {
                if ( !defined( $pwline = readline($passwd_fh) ) ) {

                    return;
                }
                @PW = split( /:/, $pwline );
                if ( scalar @PW >= $MIN_FIELDS_FOR_VALID_ENTRY && $PW[0] =~ m/^[A-Za-z0-9_.]/ && $PW[$field] eq $value ) {    # Check for valid lines and skip commented/invalid lines
                    chomp $PW[$#PW];
                    close($passwd_fh);

                    return ( $PW[0], $PW[1], $PW[2], $PW[3], '', '', $PW[4], $PW[5], $PW[6], -1, -1, $passwdmtime, ( $shadowmtime || $passwdmtime ) );
                }
            }
        }
        close($passwd_fh);
    }
    return;
}

sub _readmasterpasswd {

    my ( $value, $field, $passwdmtime, $shadowmtime, @PW ) = ( $_[0], ( $_[1] || 0 ), ( $_[2] || ( stat('/etc/passwd') )[9] ), ( $_[3] || ( stat('/etc/master.passwd') )[9] ) );
    if ( open my $master_fh, '<', '/etc/master.passwd' ) {
        while ( my $line = <$master_fh> ) {
            chomp $line;
            my @PW = split( /:/, $line );
            if ( @PW && defined( $PW[$field] ) && $PW[$field] eq $value ) {
                close $master_fh;

                return ( $PW[0], $PW[1], $PW[2], $PW[3], $PW[5], $PW[4], $PW[7], $PW[8], $PW[9], $PW[6], $PW[5], $passwdmtime, $shadowmtime );
            }
        }
        close $master_fh;
    }
    else {
        Carp::cluck("Unable to open /etc/master.passwd: $!");
    }
    return;
}

sub _pwfunc {
    my ( $value, $field, $useipc, $pwkey ) = ( $_[0], ( $_[1] || 0 ), $_[2], $_[1] . ':' . ( $_[0] || 0 ) );
    return if ( !defined $value );

    if ($pwcacheistied) {

        $Cpanel::Debug::level > 3 && print STDERR __PACKAGE__ . "::_pwfunc cache tie (tied) value[$value] field[$field]\n";
        return $pwcachetie->{$pwkey}->{'contents'};
    }

    my ( $passwdmtime, $hpasswdmtime ) = (
          ( stat('/etc/passwd') )[9], ( $system eq 'freebsd' && -r '/etc/master.passwd' )
        ? ( stat(_) )[9]
        : ( -r '/etc/shadow' ? ( stat(_) )[9] : 0 )
    );

    if ( exists $pwcache_ref->{$pwkey} ) {
        $Cpanel::Debug::level > 3 && print STDERR __PACKAGE__ . "::_pwfunc exists in cache value[$value] field[$field]\n";
        if ( $hpasswdmtime && ( $hpasswdmtime != $pwcache_ref->{$pwkey}->{'hcachetime'} )
            || ( $passwdmtime && $passwdmtime != $pwcache_ref->{$pwkey}->{'cachetime'} ) ) {    #timewarp safe
            $Cpanel::Debug::level > 3 && print STDERR __PACKAGE__ . "::_pwfunc cache miss value[$value] field[$field] pwkey[$pwkey] " . qq{hpasswdmtime: $hpasswdmtime != $pwcache_ref->{$pwkey}->{'hcachetime'} } . qq{passwdmtime: $passwdmtime != $pwcache_ref->{$pwkey}->{'cachetime'} } . "\n";

            if ( defined $pwcache_ref->{$pwkey} && defined $pwcache_ref->{$pwkey}->{'contents'} ) {
                delete @$pwcache_ref{ keys %$pwcache_ref };                                     #nuke it as its no longer valid
            }
        }
        elsif ( exists( $pwcache_ref->{$pwkey}->{'contents'} ) ) {
            $Cpanel::Debug::level > 3 && print STDERR __PACKAGE__ . "::_pwfunc cache hit value[$value] field[$field]\n";

            return $pwcache_ref->{$pwkey}->{'contents'};
        }
    }

    $Cpanel::Debug::level > 3 && print STDERR __PACKAGE__ . "::_pwfunc cache miss pwkey[$pwkey] value[$value] field[$field] passwdmtime[$passwdmtime] pwcacheistied[$pwcacheistied] hpasswdmtime[$hpasswdmtime]\n";
    my $pwdata = _getpwdata( $value, $field, $passwdmtime, $hpasswdmtime );
    if ( $pwdata && @$pwdata ) {
        @{ $pwcache_ref->{$pwkey} }{ 'cachetime', 'hcachetime', 'contents' } = ( $passwdmtime, $hpasswdmtime, $pwdata );

        if ( $field eq '0' ) {    #if we getpwnam, then cache the getpwuid info as well
            @{ $pwcache_ref->{ '2' . ':' . $pwdata->[2] } }{ 'cachetime', 'hcachetime', 'contents' } = ( $passwdmtime, $hpasswdmtime, $pwdata );
        }
        elsif ( !$skip_uid_cache && $field eq '2' ) {
            @{ $pwcache_ref->{ '0' . ':' . $pwdata->[0] } }{ 'cachetime', 'hcachetime', 'contents' } = ( $passwdmtime, $hpasswdmtime, $pwdata );
        }
    }
    return $pwdata;
}

sub init_pwcache {
    return _build_pwcache();
}

sub init_passwdless_pwcache {
    return _build_pwcache( 'nopasswd' => 1 );
}

sub fetch_pwcache {
    init_passwdless_pwcache() if !$pwcache_inited;
    return [ map { $pwcache_ref->{$_}->{'contents'} } ( $pwcache_has_uid_cache ? grep( !/^0:/, keys %{$pwcache_ref} ) : ( keys %{$pwcache_ref} ) ) ];
}

sub _build_pwcache {
    my %OPTS = @_;
    my ( $cache_file, $passwdmtime, $cache_file_mtime, $epasswd_ref, $epasswd_file, $hpasswdmtime ) = ( '/etc/passwd.cache', ( stat('/etc/passwd') )[9] );
    if ( $OPTS{'nopasswd'} ) {

        $hpasswdmtime = ( $system eq 'freebsd' && -r '/etc/master.passwd' ) ? ( stat(_) )[9] : ( stat('/etc/shadow') )[9];
        $cache_file = '/etc/passwd' . ( $skip_uid_cache ? '.nouids' : '' ) . '.cache';
    }
    elsif ( $system eq 'freebsd' && -r '/etc/master.passwd' ) {
        $hpasswdmtime = ( stat(_) )[9];
        $epasswd_file = '/etc/master.passwd';
        $cache_file   = '/etc/master.passwd' . ( $skip_uid_cache ? '.nouids' : '' ) . '.cache';
    }
    elsif ( -r '/etc/shadow' ) {
        $hpasswdmtime = ( stat(_) )[9];
        $epasswd_file = '/etc/shadow';
        $cache_file   = '/etc/shadow' . ( $skip_uid_cache ? '.nouids' : '' ) . '.cache';
    }
    else {
        $hpasswdmtime = 0;
    }

    if ( !$pwcacheistied && exists $INC{'Storable.pm'} ) {
        $cache_file_mtime = ( stat($cache_file) )[9] || 0;
        if ( $cache_file_mtime > $hpasswdmtime && $cache_file_mtime > $passwdmtime ) {
            if ( open( my $cache_fh, '<', $cache_file ) ) {
                my $cache_ref;

                eval 'local $SIG{__DIE__}; local $SIG{__WARN__}; $cache_ref = Storable::pretrieve($cache_fh);';    #must be quoted or it ends up in the stash
                close($cache_fh);
                $Cpanel::Debug::level > 3 && print STDERR "[read pwcache from $cache_file]\n";
                if (   $cache_ref
                    && ( scalar keys %{$cache_ref} ) > 0
                    && $cache_ref->{'0:root'}->{'hcachetime'} == $hpasswdmtime
                    && $cache_ref->{'0:root'}->{'cachetime'} == $passwdmtime ) {
                    $Cpanel::Debug::level > 3 && print STDERR "[validated pwcache from $cache_file]\n";
                    %{$pwcache_ref} = ( %{$pwcache_ref}, %{$cache_ref} );
                    $pwcache_inited = ( $OPTS{'nopasswd'} ? 1 : 2 );
                    return;
                }

            }
        }
    }

    if ($epasswd_file) { $epasswd_ref = _load_pws($epasswd_file); }
    $pwcache_inited        = ( $OPTS{'nopasswd'} ? 1 : 2 );
    $pwcache_has_uid_cache = ( $skip_uid_cache   ? 0 : 1 );

    if ( open( my $pwcache_passwd_fh, '<', '/etc/passwd' ) ) {
        local $/;
        $pwcache_ref = {
            map {
                $_ = [ split( /:/, $_ ) ];
                scalar @$_ >= $MIN_FIELDS_FOR_VALID_ENTRY && $_->[0] =~ m/^[A-Za-z0-9_]/
                  ? (
                    '0:' . $_->[0] => { 'cachetime' => $passwdmtime, 'hcachetime' => $hpasswdmtime, 'contents' => [ $_->[0], ( exists $epasswd_ref->{ $_->[0] } ? $epasswd_ref->{ $_->[0] } : $_->[1] ), $_->[2], $_->[3], '', '', $_->[4], $_->[5], $_->[6], -1, -1, $passwdmtime, $hpasswdmtime ] },
                  )
                  : ()
              }
              split( /\n/, readline($pwcache_passwd_fh) )
        };
        if ( !$skip_uid_cache ) {
            %$pwcache_ref = (
                %$pwcache_ref,
                map { '2:' . $pwcache_ref->{$_}{'contents'}->[2] => $pwcache_ref->{$_} } keys %$pwcache_ref
            );
        }
        close($pwcache_passwd_fh);
    }

    if ( !$pwcacheistied && exists $INC{'Storable.pm'} && ref $pwcache_ref ) {
        if ( !$cache_file_mtime
            && open( my $cache_fh, '>', $cache_file ) ) {
            chmod( 0600, $cache_file );
            close($cache_fh);
        }
        $Cpanel::Debug::level > 3 && print STDERR "[wrote pwcache to $cache_file]\n";
        my $pwlock = Cpanel::SafeFile::safeopen( \*PWCACHEFILE, '>', $cache_file );
        if ($pwlock) {
            eval 'Storable::nstore_fd( $pwcache_ref, \*PWCACHEFILE  );';    #must be quoted or it ends up in the stash
            Cpanel::SafeFile::safeclose( \*PWCACHEFILE, $pwlock );
        }
    }

    return;
}

sub pwcache_is_initted {
    return ( $pwcache_inited ? $pwcache_inited : 0 );
}

sub _getpwdata {
    my ( $value, $field, $passwdmtime, $shadowmtime ) = @_;
    return if ( !defined $value || !defined $field );

    my $myuid = $>;
    $Cpanel::Debug::level > 3 && print STDERR __PACKAGE__ . "::_getpwdata called for myuid[$myuid] value[$value] field[$field]\n";

    if ( !$pwdatafunc || $myuid != $pwdatafuncuid ) {
        $pwdatafuncuid = $myuid;
        if ( $system eq 'freebsd' ) {
            $pwdatafunc = ( -r '/etc/master.passwd' ? \&_readmasterpasswd : \&_readpasswd );
        }
        else {
            $pwdatafunc = ( -r '/etc/shadow' ? \&_readshadow : \&_readpasswd );
        }
    }
    return [ $pwdatafunc->( $value, $field, $passwdmtime, $shadowmtime ) ];
}

sub _load_pws {
    my $lookup_file = shift;

    my %PW;
    if ( open my $lookup_fh, '<', $lookup_file ) {
        seek( $lookup_fh, 0, 0 );
        local $/;
        %PW = map { ( split( /:/, $_ ) )[ 0, 1 ] } split( /\n/, readline($lookup_fh) );
        delete @PW{ '', grep( m/^#/, keys %PW ) };
        close $lookup_fh;
    }
    return \%PW;
}

{
    no warnings 'once';
    *get_line_from_pwfile = \&_get_line_from_pwfile;
}

sub _get_line_from_pwfile {
    my ( $lookup_file, $lookup_key, $is_case_insensitive ) = @_;

    my @PW;
    if ( open my $lookup_fh, '<', $lookup_file ) {
        seek( $lookup_fh, 0, 0 );
        my $search_regex = $is_case_insensitive ? qr/^\Q$lookup_key\E:/i : qr/^\Q$lookup_key\E:/;
        while ( readline($lookup_fh) ) {
            if ( $_ =~ $search_regex ) {
                chomp();
                @PW = split( /:/, $_ );
                last;
            }
        }
        close $lookup_fh;
    }
    return \@PW;
}

sub _get_keyvalue_from_pwfile {
    my ( $lookup_file, $key_position, $lookup_key, $is_case_insensitive ) = @_;

    my $pwref = _get_line_from_pwfile( $lookup_file, $lookup_key, $is_case_insensitive );
    if ( defined $pwref && @{$pwref} ) {
        return $pwref->[$key_position];
    }
}

sub beginmatch {

    return ( substr( $_[0], 0, length( $_[1] ) ) eq $_[1] ) ? 1 : 0;
}

sub dump_rec {
    my $rec = shift;

    if ( ref $rec eq 'HASH' ) {
        my $buf;
        foreach my $key ( keys %{$rec} ) {
            if ( ref $rec->{$key} eq 'ARRAY' ) {
                $buf .= "$key=" . join( ',', @{ $rec->{$key} } ) . "\t";
            }
            else {
                $buf .= "$key=$rec->{$key}\t";
            }
        }
        return $buf;
    }
    else {
        return $rec;
    }
}

sub get_gid_cacheref {
    my $grplock = Cpanel::SafeFile::safeopen( \*GRPFILE, '<', '/etc/group' );
    return if !$grplock;
    local $/;
    my %GIDCACHE = map { defined $_->[2] ? ( $_->[2] => $_ ) : () } map { [ split( /:/, $_ ) ] } split( /\n/, readline( \*GRPFILE ) );
    Cpanel::SafeFile::safeclose( \*GRPFILE, $grplock );
    return \%GIDCACHE;
}

sub load_pw_cache_file {
    my ( $file, $passwduid, $passwdmtime, $no_uidcheck, $keepforever ) = @_;

    if ( !defined $passwduid || !defined $passwdmtime ) {
        ( $passwduid, $passwdmtime ) = ( stat($file) )[ 4, 9 ];
    }
    if ( !$INC{'Storable.pm'} || ( !$no_uidcheck && $passwduid != 0 ) || !$passwdmtime ) { return; }
    my $pwdata_ref;
    if ( open( my $pw_file_fh, '<', $file ) ) {
        eval ' local $SIG{__DIE__}; local $SIG{__WARN__}; $pwdata_ref = Storable::pretrieve($pw_file_fh); ';
        close($pw_file_fh);
    }
    if ( !$pwdata_ref || !ref $pwdata_ref ) { return; }
    my $pwdata = ( ref $pwdata_ref eq 'ARRAY' ? $pwdata_ref : $pwdata_ref->{'contents'} );
    if ( !$pwdata || ref $pwdata ne 'ARRAY' ) { return; }

    if ($keepforever) {
        $pwdata->[11] = 2147483647;
        $pwdata->[12] = 2147483647;
    }

    @{ $pwcache_ref->{ '2' . ':' . $pwdata->[2] } }{ 'cachetime', 'hcachetime', 'contents' } = ( $pwdata->[11], $pwdata->[12], $pwdata );
    @{ $pwcache_ref->{ '0' . ':' . $pwdata->[0] } }{ 'cachetime', 'hcachetime', 'contents' } = ( $pwdata->[11], $pwdata->[12], $pwdata );
    return $pwdata->[0];
}

sub invalidate {
    my $keyname = shift;
    if    ( $keyname eq 'user' ) { $keyname = 0; }
    elsif ( $keyname eq 'uid' )  { $keyname = 2; }
    my $key = shift;

    $keyname = int $keyname;
    $key =~ s/\///g;

    my $pwkey = $keyname . ':' . $key;

    if ( exists $pwcache_ref->{$pwkey} ) {
        delete $pwcache_ref->{$pwkey};
    }

    if ( $keyname == 0 ) {
        unlink( '/var/cpanel/@pwcache/' . $key );
    }
    unlink( '/var/cpanel/pw.cache/' . $pwkey );
    unlink(
        '/etc/master.passwd.cache',
        '/etc/master.passwd.nouids.cache',
        '/etc/passwd.cache',
        '/etc/passwd.nouids.cache',
        '/etc/shadow.cache',
        '/etc/shadow.nouids.cache',
    );
}

sub getusername {

    my $user = defined $_[0] ? $_[0] : $>;
    return ( Cpanel::PwCache::getpwuid($user) )[0];
}

sub gethomedir {

    my $user = defined $_[0] ? $_[0] : $>;
    return ( Cpanel::PwCache::getpwuid($user) )[7];
}

1;

} # --- END Cpanel/PwCache.pm


{ # --- BEGIN Cpanel/SafeFile.pm
package Cpanel::SafeFile;


$Cpanel::SafeFile::VERSION = '2.1';


use Fcntl ();            # qw( O_WRONLY O_EXCL O_CREAT );
# use Cpanel::Logger ();

my $logger = Cpanel::Logger->new();
my $MAGIC_FCNTL_VALUE;
my $verbose; # initialized in safelock
my $MAX_FLOCK_WAIT = 60;

sub safeopen {
    my $fh = shift;
    my ( $mode, $file ) = _get_open_args(@_);

    if ( !$mode || !$file ) {
        $logger->warn('Invalid arguments');
        return;
    }
    elsif ( !defined $fh ) {
        $logger->warn("Undefined file handle provided for safeopen of $file");
        return;
    }
    else {
        my $fh_type = ref $fh;

        if ( $fh_type ne 'GLOB' && $fh_type ne 'IO::Handle' && $fh_type ne 'FileHandle' ) {
            $logger->warn("Invalid file handle type $fh_type provided for safeopen of $file");
            return;
        }
    }

    if ( my $lockref = safelock($file) ) {
        if ( open $fh, $mode, $file ) {
			if( !flock $fh, 6) { # flock 6 is LOCK_EX and LOCK_NB (non blocking exclusive lock)
                eval {
                    local $SIG{'ALRM'} = sub { die "flock LOCK_EX timeout"; };
                    my $orig_alarm = alarm $MAX_FLOCK_WAIT;
                    flock $fh, 2;    # This will hang
                    alarm $orig_alarm;
                };
			}
            return $lockref;
        }
        else {
            safeunlock($lockref);
            return;
        }
    }
    else {
        $logger->warn('could not get a lock');
        return;
    }
}

sub safeclose {
    my ( $fh, $lockref ) = @_;

    if ( defined fileno $fh ) {
        close $fh;
    }

    return safeunlock($lockref);
}

sub safe_replace_content {
    my ( $fh, @content ) = @_;
    @content = @{ $content[0] } if @_ == 2 && ref $content[0] eq 'ARRAY';
    seek( $fh, 0, 0 );
    print {$fh} @content;
    truncate( $fh, tell($fh) );
}

sub safelock {
    my($file,$lockfile,$lock_fh,$lock_file_dir,$attempts) = ($_[0]);
    if (!$file) {
        $logger->warn('safelock: Invalid arguments');
        return;
    }
    $verbose ||= (-e '/var/cpanel/safefile_verbose' ? 1 : -1);
    local $0 = "$0 - waiting for lockfile";

    if (! ($lockfile = _lock_wait($file) ) ){
		return;
	}	

    $MAGIC_FCNTL_VALUE ||= (&Fcntl::O_WRONLY | &Fcntl::O_EXCL | &Fcntl::O_CREAT);
    while ( ! sysopen( $lock_fh, $lockfile, $MAGIC_FCNTL_VALUE, 0600 ) && ++$attempts < 60 ) {
       
        if (! $lock_file_dir ) {
            if ( !-w _getdir($lockfile) ) {
                return 1;
            }
        }
        $lockfile = _lock_wait($file);
        return if !$lockfile;
    } 
    if ($lock_fh) {
        syswrite($lock_fh,$$ . "\n" . $0 . "\n");
        return [$lockfile,$lock_fh,(stat($lock_fh))[1,9]];
    }
    $logger->warn('safelock: waited for lock ' . $attempts . ' times');
    return;
}

sub safeunlock {
    my $lockref = shift;
    if ( !$lockref ) {
        $logger->warn('safeunlock: Invalid arguments');
        return;
    }
    elsif ( !ref $lockref && $lockref eq '1' ) {

        return 1;
    }
    elsif (!ref $lockref) {
        $lockref=[$lockref];
    }
	my ($filesys_lock_ino,$filesys_lock_mtime) = (lstat($lockref->[0]))[1,9];    #->[0] = file
    if (!$filesys_lock_mtime) {
        $logger->warn("Lock $lockref->[0] lost!");  #->[0] = file
		close($lockref->[1]) if ref $lockref->[1]; #->[1] = fh
        return;
    }

 	if ($lockref->[2] == $filesys_lock_ino && $lockref->[3] == $filesys_lock_mtime) {  # if the fh we have open is the same inode we are good #->[2] = inode #->[3] = mtime
		close($lockref->[1]) if ref $lockref->[1]; #->[0] = fh
        unlink $lockref->[0] or return; #->[0] = file
        return 1;
    }
    else {
	   close($lockref->[1]) if ref $lockref->[1]; #->[0] = fh
	   my ( $lock_pid, $lock_name ) = _fetch_lockfile_info($lockref->[0]); #->[0] = file
       if ( !$lock_pid ) {
            unlink $lockref->[0];
            $logger->warn("Invalid zero length lock file $lockref->[0] detected."); #->[0] = file
            return;
        } else {
        	$logger->warn("[$$] Attempt to unlock file that was locked by another process [LOCK] $lockref->[0] [LOCK PID] $lock_pid [LOCK PROCESS] $lock_name"); #->[0] = file
        	return;
		}
    }
}

sub _get_open_args {
    my ( $mode, $file ) = @_;
    if ( !$file ) {
        ( $mode, $file ) = $mode =~ m/^([<>+|]+|)(.*)/;
        if ( $file && !$mode ) {
            $mode = '<';
        }
        elsif ( !$file ) {
            return;
        }
    }

    $mode =
        $mode eq '<'  ? '<'
      : $mode eq '>'  ? '>'
      : $mode eq '>>' ? '>>'
      : $mode eq '+<' ? '+<'
      : $mode eq '+>' ? '+>'
      :                 return;

    return ( $mode, $file );
}

sub _lock_wait {
	my ( $file, $lockfile, $lockfile_inode, $lockfile_mtime ) = ( $_[0], ( $_[0] =~ /^[><]*(.*)/ )[0] . '.lock' );

	return $lockfile if ! -e $lockfile;

  
    my ( $start_lockfile_inode, $fileuid, $locksize, $start_lockfile_mtime ) = ( stat(_) )[ 1, 4, 7, 9 ];
	my $attempts = 0;
    while ( !$locksize && ++$attempts < 15) {  # wait up to 15 seconds for the lock file data to get written
        sleep(1); # must sleep one because mtime is only granular to the second
        ( $lockfile_inode , $fileuid, $locksize, $lockfile_mtime ) = ( stat($lockfile) )[ 1, 4, 7, 9 ];
        if ( ($lockfile_inode && $lockfile_inode != $start_lockfile_inode ) || ( $lockfile_mtime && $lockfile_mtime  !=  $start_lockfile_mtime )) {
            $locksize=0; 
            $start_lockfile_inode=$lockfile_inode;
            $start_lockfile_mtime=$lockfile_mtime; 
            next;     
        }
        if ( !defined $locksize) {
            return $lockfile;
        } elsif ( $locksize == 0 ) {
            $start_lockfile_mtime=$lockfile_mtime;
            $logger->invalid("Invalid lockfile $lockfile detected (zero size) [UID]: $fileuid [MTIME]: $start_lockfile_mtime [ORIGINAL INODE]: $start_lockfile_inode [TESTED INODE]: $lockfile_inode");
            last;
        }
    }
   
    my $waittime = 60;
    if ( -e $file ) {
        $waittime = int( ( stat(_) )[7] / 10000 );
        $waittime = $waittime > 350 ? 350 : $waittime < 60 ? 60 : $waittime;    # waittime is always between 60 and 350 seconds
    }

    my $lock_file_age = ( time() - $start_lockfile_mtime );

    if ( $lock_file_age > $waittime ) {
        $logger->info("Stale lock file: $lockfile. Age is $lock_file_age (mtime=$start_lockfile_mtime) which is longer then waittime ($waittime)") if $verbose == 1;
        unlink $lockfile;
        return $lockfile;
    }

    my $lock_is_our_uid = ( $fileuid == $> );

    if ( $lock_is_our_uid || $> == 0 ) {
        my $proc_is_usable = _proc_is_usable();

        my $lock_pid = 0;
        my $lock_name;
        if ( $locksize && ( $lock_is_our_uid || $proc_is_usable ) ) {    # PID is inside lock file
            ( $lock_pid, $lock_name ) = _fetch_lockfile_info($lockfile);
        }

        if ( !$lock_pid ) {
            $logger->info("[$$] Waiting on invalid lock $lockfile for $waittime seconds") if $verbose == 1;
        }
        elsif ( $lock_pid == $$ ) {
            $logger->invalid("[$$] Double locking detected on $file by self ($lock_name)");
            return;
        }
        else {
            $logger->info("[$$] Waiting for lock on $file held by $lock_name with pid $lock_pid") if $verbose == 1;
        }

        if ( _is_valid_pid($lock_pid) && ( $lock_is_our_uid || ( $proc_is_usable && -e '/proc/' . $lock_pid ) ) ) {
            my $seconds_waiting = 0;
            while (1) {
                sleep 1;
                $seconds_waiting++;
                last if ( $seconds_waiting > $waittime );
                last if !-e $lockfile;
               
                if ($lock_is_our_uid) {

                    if ( !kill( 0, $lock_pid ) ) {
                        last;
                    }
                }
               
                elsif ( $proc_is_usable && !-e '/proc/' . $lock_pid ) {
                    last;
                }
            }        

            my $mtime = ( stat($lockfile) )[9];
            if ( !$mtime ) {
                $logger->info("[$$] Lock file $lockfile now gone, try to acquire") if $verbose == 1;
                return $lockfile;
            }
            if ( $mtime == $start_lockfile_mtime ) {
                $logger->info("[$$] Removing expired lock file $lockfile") if $verbose == 1;
                unlink $lockfile;
                return $lockfile;
            }
            else {
                $start_lockfile_mtime = $mtime;
            }
        }
    }

    my $seconds_waiting = 0;
  WAIT:
    while ( $start_lockfile_mtime > 0 ) {
        if ( -e $lockfile ) {
            my $mtime = ( stat(_) )[9];
            sleep 1;
            if ( $mtime == $start_lockfile_mtime ) {
                $seconds_waiting++;    # lock file has aged
            }
            else {                     # there is a new lock file, reset
                $start_lockfile_mtime = $mtime;
                $seconds_waiting      = 0;
            }
            if ( $seconds_waiting >= $waittime ) {    # lock file aged waittime sec and we can stop waiting
                $logger->info("Lock file $lockfile expired") if $verbose == 1;
                unlink $lockfile;
                last WAIT;
            }
        }
        else {
           
            last WAIT;
        }
    }
	return $lockfile;
}

sub safe_readwrite {
    my ( $file, $code_ref ) = @_;

    return if !defined $file || $file eq '' || ref $code_ref ne 'CODE';

    if ( my $lockref = safeopen( \*SAFEEDIT, '+<', $file ) ) {

        my $rclog = $code_ref->( \*SAFEEDIT, \&safe_replace_content );

        safeclose( \*SAFEEDIT, $lockref );

        if ($rclog && $rclog ne '0E0') {
            eval q{
                require Cpanel::RcsRecord;
                Cpanel::RcsRecord::rcsrecord( $file, $rclog );
            };
        }

        return $rclog;
    }
    else {
        return;
    }
}

sub _proc_is_usable {
    return ( -e '/proc/1' && -r _ ) ? 1 : 0;
}

sub _fetch_lockfile_info {
    my $lockfile = shift;
    my ( $lock_pid, $lock_name );
    if ( open my $lockfile_fh, '<', $lockfile ) {
		local $/;
        my ($pid_line,$lock_name) = split(/\n/, readline($lockfile_fh));
        chomp($lock_name);
        ($lock_pid) = $pid_line =~ m/(\d+)/;
        close $lockfile_fh;
        return ( $lock_pid, $lock_name || 'unknown' );
    }
}

sub _is_valid_pid {
    my $pid = shift;

    return ( $pid =~ /^\d+$/ ? 1 : 0 );
}

sub _getdir {
    my $file = shift;
    my @path = split( /\/+/, $file );
    pop(@path);
    return join( '/', @path );
}

1;


} # --- END Cpanel/SafeFile.pm


{ # --- BEGIN Cpanel/RcsRecord.pm
package Cpanel::RcsRecord;


# use Cpanel::SafeFile       ();
# use Cpanel::FindBin        ();
# use Cpanel::SafeRun::InOut ();

our $SKIP_IF_MTIME_MATCHES = 2;
our $VERSION               = '4.3';

sub rcsrecord {
    my ( $file, $description, $skip_lock, $flags ) = @_;
    if ( !$description ) {
        $description = '';
    }

    my ( $uid, $gid, $size, $mtime ) = ( stat($file) )[ 4, 5, 7, 9 ];
    if ( !$mtime ) { return; }

    my $maxsize = ( $size * 25 );
    if ( $maxsize < 32768 ) { $maxsize = 32768; }

    my @DIR      = split( /\//, $file );
    my $truefile = pop(@DIR);
    my $dir      = '';
    if ( $#DIR > -1 ) {
        $dir = join( '/', @DIR );
    }
    if ( !$dir ) {
        $dir = '.';
    }

    if ( $uid != $> ) {
        my $dir_uid = ( stat($dir) )[4];
        if ( $dir_uid != $> ) {
            warn "rcsrecord skipped on $file because we do not own it or the directory it is in";
            return;
        }
    }

    my $rcslock;
    if ( !$skip_lock ) {
        $rcslock = Cpanel::SafeFile::safelock($file);
        if ( !$rcslock ) {

            return;
        }
    }
    if ( -e "$dir/,$truefile," ) {
        my $tmp_mtime = ( stat("$dir/,$truefile,") )[9];
        if ( ( time() - $tmp_mtime ) > 360 ) {    #remove temp files after 5 minutes
            unlink("$dir/,$truefile,");
        }
    }

    my $rcs_bin;

    if ( !-e "$dir/$truefile,v" ) {
        $rcs_bin = Cpanel::FindBin::findbin('rcs');
        if ($rcs_bin) {
            if ( my $rcs_pid = fork() ) {
                waitpid( $rcs_pid, 0 );
            }
            elsif ( defined $rcs_pid ) {
                open( STDOUT, '>&STDERR' );
                open( STDIN, '<', '/dev/null' );
                exec( $rcs_bin, '-q', '-i', $file );
                exit 1;
            }
            else {

                if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
                return;
            }
        }
        else {
            if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
            return;
        }
    }
    elsif ( $flags & $SKIP_IF_MTIME_MATCHES ) {
        if ( $mtime && $mtime <= ( stat(_) )[9] ) {
            if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
            return;
        }
    }

    my $ci_bin = Cpanel::FindBin::findbin('ci');
    if ($ci_bin) {
        my $safe_msg = "Modified by $0 $description";
        $safe_msg =~ s/\"//g;
        if ( my $ci_pid = fork() ) {
            waitpid( $ci_pid, 0 );
        }
        elsif ( defined $ci_pid ) {
            open( STDIN,  '<', '/dev/null' );
            open( STDOUT, '>', '/dev/null' );
            open( STDERR, '>', '/dev/null' );
            exec( $ci_bin, '-l', qq{-m"$safe_msg"}, $file );
            exit 1;
        }
        else {    #fork failed
            if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
            return;
        }
    }
    else {
        if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
        return;
    }

    if ( ( stat("$dir/$truefile,v") )[7] > $maxsize ) {
        my $rlog_bin = Cpanel::FindBin::findbin('rlog');
        my @REVS;
        if ($rlog_bin) {
            my $head;
            my $rlog_pid = Cpanel::SafeRun::InOut::inout( my $wtr_rlog, my $rdr_rlog, $rlog_bin, $file );
            if ($rlog_pid) {
                close($wtr_rlog);
                while ( readline($rdr_rlog) ) {
                    if (/revision[\s\t]+([\d\.]+)/) {
                        push @REVS, $1;
                    }
                }
                close($rdr_rlog);    # no waitpid needed because inout will use an open call which will waitpid automaticlly when this is closed
            }
            else {
                if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
                return;
            }
        }
        else {
            if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
            return;
        }
        if (@REVS) {
            my $rev = $REVS[ int( $#REVS / 2 ) ];    #remove 50% of revisions
            if ( !$rcs_bin ) { $rcs_bin = Cpanel::FindBin::findbin('rcs'); }
            if ( !$rcs_bin ) {
                if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
                return;
            }
            if ( my $rcs_pid = fork() ) {
                waitpid( $rcs_pid, 0 );
            }
            elsif ( defined $rcs_pid ) {
                open( STDOUT, '>&STDERR' );
                open( STDIN, '<', '/dev/null' );
                exec( $rcs_bin, '-q', '-o:' . $rev, $file );
                exit 1;
            }
            else {

                if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
                return;
            }
        }
    }
    if ($rcslock) { Cpanel::SafeFile::safeunlock($rcslock); }
    chown $uid, $gid, $file;
}

1;

} # --- END Cpanel/RcsRecord.pm


{ # --- BEGIN Cpanel/FindBin.pm
package Cpanel::FindBin;


use strict;
our $VERSION = 1.2;

my %bin_cache;
my @default_path = qw( /usr/bin /usr/local/bin /bin /sbin /usr/sbin /usr/local/sbin );

sub findbin {
    my $binname = shift;
    return if !$binname;

    my @lookup_path = get_path(@_);

    my $nocache = grep( /nocache/, @_ );

    if ( !$nocache && exists $bin_cache{$binname} && $bin_cache{$binname} ne '' ) {
        return $bin_cache{$binname};
    }

    foreach my $path (@lookup_path) {
        if ( -x $path . '/' . $binname ) {
            $bin_cache{$binname} = $path . '/' . $binname unless $nocache;
            return $path . '/' . $binname;
        }
    }
    return;
}

sub get_path {
    if ( !$_[0] ) {
        return @default_path;
    }
    elsif ( scalar @_ > 1 ) {
        my %opts = @_;
        if ( exists $opts{'path'} && ref $opts{'path'} eq 'ARRAY' ) {
            return @{ $opts{'path'} };
        }
        else {
            return @_;
        }
    }
    elsif ( ref $_[0] eq 'ARRAY' ) {
        return @{ $_[0] };
    }
    return @default_path;
}

1;

} # --- END Cpanel/FindBin.pm


{ # --- BEGIN Cpanel/SafeRun/InOut.pm
package Cpanel::SafeRun::InOut;


use Symbol ();

sub inout {
    my $wtr = $_[0] ||= Symbol::gensym();
    my $rdr = $_[1] ||= Symbol::gensym();

    my $child_read;

    pipe( $child_read, $wtr );

    select( ( select($wtr),        $| = 1 )[0] );    #aka $wtr->autoflush(1);
    select( ( select($child_read), $| = 1 )[0] );    #aka $child_read->autoflush(1);

    if ( my $pid = open( $rdr, '-|' ) ) {
        return $pid;
    }
    elsif ( defined $pid ) {
        open( STDIN, '<&=' . fileno($child_read) );
        if ( ref $_[2] eq 'CODE' ) {
            $_[2]->();
            exec( @_[ 3 .. $#_ ] );
        }
        else {
            exec( @_[ 2 .. $#_ ] );
        }
        exit 1;
    }
    else {
        return;
    }
}

1;

} # --- END Cpanel/SafeRun/InOut.pm


{ # --- BEGIN Cpanel/Debug.pm

package Cpanel::Debug;

our $level = ( exists $ENV{'CPANEL_DEBUG_LEVEL'} && $ENV{'CPANEL_DEBUG_LEVEL'} ? int $ENV{'CPANEL_DEBUG_LEVEL'} : 0 );

1;

} # --- END Cpanel/Debug.pm


{ # --- BEGIN Cpanel/AccessIds/SetUids.pm
package Cpanel::AccessIds::SetUids;


# use Cpanel::PwCache ();
# use Cpanel::Logger  ();

our $VERSION = '1.2';

sub setuids {
    my ( $user, $group, @additional_groups ) = @_;
    my ( $uid, $gid );

    if ( $< != 0 ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Attempting to setuid as a normal user with RUID $<");
    }

    if ( $> != 0 ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Attempting to setuid as a normal user with EUID $>");
    }

    if ( $user !~ m/^\d+$/ ) {
        ( $uid, $gid ) = ( Cpanel::PwCache::getpwnam($user) )[ 2, 3 ];
    }
    else {
        $uid = $user;
    }

    if ( $group && $group !~ /^\d+$/ ) {
        $gid = ( Cpanel::PwCache::getpwnam($user) )[3];
    }
    elsif ( $group && $group =~ m/^\d+$/ ) {
        $gid = $group;
    }
    elsif ( !defined $gid ) {
        $gid = ( Cpanel::PwCache::getpwuid($uid) )[3];
    }

    Cpanel::PwCache::pwmksafecache();

    if ( !defined $uid || !defined $gid ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Could not resolve UID ($uid) or GID ($gid)");
    }

    $gid = int($gid);

    $( = $gid;
    if ( $gid != $( ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Error setting RGID ($gid) [$user]");
    }

    $) = join( ' ', $gid, $gid, ( map { /^\d+$/ ? $_ : ( ( getgrgid($_) )[2] || () ) } @additional_groups ) );
    if ( $gid != int($)) ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Error setting EGID ($gid) [$user]");
    }

    $< = $uid;
    if ( $< != $uid ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Error setting RUID ($uid) [$user]");
    }

    $> = $uid;
    if ( $> != $uid ) {
        my $cplog = Cpanel::Logger->new();
        $cplog->die("Error setting EUID ($uid) [$user]");
    }

    return $uid;
}

1;

} # --- END Cpanel/AccessIds/SetUids.pm


#!/usr/bin/perl
##################################################################################
# cpanel - upcp                                   Copyright(c) 2011 cPanel, Inc. #
#                                                           All rights Reserved. #
# copyright@cpanel.net                                         http://cpanel.net #
# This code is subject to the cPanel license. Unauthorized copying is prohibited #
##################################################################################

BEGIN {
    ;
    if ( !$^C ) {    # 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
        if ( $0 !~ m/upcp\.static/ ) {    # static never gets --use-checked and should pass all the begin block checks
            if ( !grep { $_ eq '--use-check' } @ARGV ) {
                system('/scripts/upcp --use-check');
                if ( $? != 0 ) {
                    print "We determined that /scripts/upcp had compilation issues..\n";
                    print "Trying to exec /usr/local/cpanel/scripts/upcp.static " . join( ' ', @ARGV ) . "\n";
                    exec( '/usr/local/cpanel/scripts/upcp.static', @ARGV );
                }
            }
        }
    }
}

# Prevent us from running script if we are the instance called from the begin block to check compilation
INIT {
    if ( grep { $_ eq '--use-check' } @ARGV ) {
        exit;
    }
}

require 5.006;
use strict;
# use Cpanel::Env             ();
# use Cpanel::WHMAPI          ();
# use Cpanel::ProcessCheck    ();
# use Cpanel::SafeRun::Errors ();
# use Cpanel::Update::Logger  ();
use IO::Handle              ();
use POSIX                   ();

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

## case 47517: move root check and setupenv higher where it was 11.28
if ( $> != 0 ) {
    die "upcp must be run as root";
}

setupenv();

#############################################################################
# Record the arguments used when started, check for certain flags

my @retain_argv = @ARGV;

my $fromself = 0;
my $forced   = 0;
my $usejs2   = 0;

my $now          = time();
my $logfile_path = '/var/cpanel/updatelogs/update.' . $now . '.log';

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 'usejs2' ) {
        $usejs2 = 1;
    }
}

# Set the progress bar if $usejs2 and depending on  $fromself, set it to a higher percentage?
my %pbar_starting_point;
if ($usejs2) {
    $pbar_starting_point{'pbar'} = 0;
    $pbar_starting_point{'pbar'} = 17 if ($fromself);
}

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

my $logger = Cpanel::Update::Logger->new( { 'logfile' => $logfile_path, 'stdout' => 1, 'log_level' => 'info', %pbar_starting_point } );

#############################################################################
# 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 $output;
my $pidfile = '/var/run/upcp.pid';
my $security_token = $ENV{'cp_security_token'} || '';

my %HTML_ENCODE_MAP = ( '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;' );

#############################################################################
# This futex stuff can be removed in 11.32
## case 47517: this clause is duplicated in maintenance; used to be alongside a conditional
##   futexfix invocation, which is now in maintenance
if ( -e '/var/cpanel/brokenfutex' ) {
    $ENV{'LD_ASSUME_KERNEL'} = q{2.4.1};
}

#############################################################################
# Make sure we aren't already running

if ( !$fromself && -e '/var/cpanel/upcpcheck' ) {
    my %PPIDS = Cpanel::ProcessCheck::previouspids( 'process' => 'upcp' );
    my $parent = getppid();
    delete $PPIDS{$parent};
    delete $PPIDS{$$};

    #
    if (%PPIDS) {    # Already running
        my $pidfile_mtime = ( stat($pidfile) )[9];
        my $pidfile_age   = ( time - $pidfile_mtime );
        if ( $pidfile_age > 21600 ) {    # Running for > 6 hours
            foreach my $formerpid ( keys %PPIDS ) {
                $logger->info("Killing former upcp process \"$formerpid\", starting new..");
                kill_upcp($formerpid);
            }
        }
        else {
            $logger->error("cPanel Update (upcp) is already running. Please wait for the previous upcp to complete, or kill off all upcp processes and try again. You may wish to use '--force'");
            exit 1;
        }
    }
}

#############################################################################
# check for running child processes

foreach my $update_process (qw {maintenance updatenow post_sync_cleanup }) {
    my %MPIDS = Cpanel::ProcessCheck::previouspids( 'process' => $update_process );

    # No pids no check.
    next if ( !%MPIDS );

    my $pidfile_mtime = ( stat($pidfile) )[9];
    my $pidfile_age   = ( time - $pidfile_mtime );
    if ( $pidfile_age > 57600 ) {    # Running for > 16 hours
        foreach my $formerpid ( keys %MPIDS ) {
            $logger->info("Killing former $update_process process \"$formerpid\", starting new..");
            kill 9, $formerpid;
        }
    }
    else {
        $logger->error("cPanel $update_process is already running. Please wait for the previous $update_process to complete, or kill off all $update_process processes and try again.");
        exit 1;
    }
}

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

my %PPIDS = Cpanel::ProcessCheck::previouspids( 'process' => 'easyapache' );
if ( scalar keys %PPIDS ) {
    $logger->error("EasyApache is currently running. Please wait for EasyApache to complete before running cPanel Update (upcp).");
    exit 1;
}

#############################################################################
# Sync the system clock if ntpd is not running

%PPIDS = Cpanel::ProcessCheck::previouspids( 'process' => 'ntpd' );
if ( !scalar keys %PPIDS ) {
    system( '/usr/bin/rdate', '-s', 'rdate.cpanel.net' );
}

#############################################################################
# Write pid and get variables needed for update

if ( open my $pid_fh, '>', $pidfile ) {
    print {$pid_fh} $$ . "\n";
    close $pid_fh;
}
else {
    $logger->error("Failed to write upcp pid file: $!");
    exit 1;
}

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

my $dnsonly = -e '/var/cpanel/dnsonly' ? 1 : 0;

## case 46528: license checks moved to updatenow and Update.pm

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

my $upcp_disallowed_path = '/root/.upcp_controlc_disallowed';

#############################################################################
# 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 ) {
    if ( my $updatepid = fork() ) {
        monitor_upcp( $updatepid, $logger );
        exit;
    }
}

$0 = 'cPanel Update (upcp) - Slave';
open( RNULL, '<', '/dev/null' );
chdir '/';

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" );
open( STDERR, ">/dev/null" );

#############################################################################
# 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';
}

$logger->increment_pbar;

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

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 recieved, pass it on to updatenow
    if ($forced) { push( @updatenow_args, '--force' ); }

    # set flag to disallow ^C during updatenow
    open( TMPF, '>>', $upcp_disallowed_path );
    close(TMPF);

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

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

    $logger->increment_pbar(15);

    if ( $exit_code != 0 ) {
        my $msg = "Running `@updatenow_args` failed, exited with code $exit_code";
        $logger->error($msg);
        eval { require Cpanel::iContact; };
        if ( exists $INC{'Cpanel/iContact.pm'} ) {
            Cpanel::iContact::icontact(
                'application' => 'upcp',
                'subject'     => 'Cpanel update failure in upcp',
                'message'     => $msg,
                'level'       => 1
            );
        }
        else {
            $logger->error('Failed to send contact message');
        }
        exit($exit_code);
    }

    # 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");
        exec '/usr/local/cpanel/scripts/upcp', @retain_argv, '--fromself', "--log=$logfile_path";
    }
}

#############################################################################
# Run the maintenance script
$logger->update_pbar(20);

my @usejs2arg;
if ( $usejs2 == 1 ) {
    push @usejs2arg, '--usejs2=' . $logger->get_pbar;
}

# Maintenance pbar range: 20-80%
$logger->close_log();    # Allow maintenance to write to the log
my $exit_status = system( '/usr/local/cpanel/scripts/maintenance', '--log=' . $logfile_path, @usejs2arg );
$logger->open_log;       # Re-open the log now maintenance is done.
$logger->update_pbar(80);

if ( $exit_status != 0 ) {
    my $msg = "Maintenance ended, however it did not exit cleanly ($exit_status). Please check the logs for an indication of what happened";
    $logger->error( $msg, 1 );
    eval { require Cpanel::iContact; };
    if ( exists $INC{'Cpanel/iContact.pm'} ) {
        Cpanel::iContact::icontact(
            'application' => 'upcp',
            'subject'     => 'Cpanel update failure in upcp',
            'message'     => $msg,
            'level'       => 1
        );
    }
    else {
        $logger->error('Failed to send contact message');
    }
}
else {
    $logger->info("Maintenance completed successfully");
}

# Run post-sync cleanup only if updatenow did a sync
# Formerly run after layer2 did a sync.
my $version_mtime = ( stat('/usr/local/cpanel/version') )[9];
if ( $fromself || $version_mtime > $now ) {

    # the post_sync_cleanup script handles the last 18-19% of the update process, so just start at 81%.
    # if this isn't ok, the maintenance script can return the pbar percent as it's exit code and we can get it there easily
    if ( $usejs2 == 1 ) {
        $usejs2arg[0] = '--usejs2=80';
    }

    # post_sync pbar range: 80%-95%
    $logger->close_log();    # Yield the log to post_sync_cleanup
    my $post_exit_status = system( '/usr/local/cpanel/scripts/post_sync_cleanup', '--log=' . $logfile_path, @usejs2arg );
    $logger->open_log;       # reopen the log to continue writing messages

    if ( $post_exit_status != 0 ) {
        my $msg = "\nPost-sync cleanup has ended, however it did not exit cleanly. Please check the logs for an indication of what happened";
        $logger->error($msg);
        eval { require Cpanel::iContact; };
        if ( exists $INC{'Cpanel/iContact.pm'} ) {
            Cpanel::iContact::icontact(
                'application' => 'upcp',
                'subject'     => 'Cpanel update failure in upcp',
                'message'     => $msg,
                'level'       => 1
            );
        }
        else {
            $logger->error('Failed to send contact message');
        }
    }
    else {
        $logger->info("Post-sync cleanup completed successfully");
    }

}

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

$logger->update_pbar(95);
if ( -x '/usr/local/cpanel/scripts/postupcp' ) {
    system '/usr/local/cpanel/scripts/postupcp';
}
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");
$logger->close_log();
exit(0);

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

# modified version of Cpanel::Encoder::Tiny::safe_html_encode_str()
sub cleanse {
    my $data = join( '', @_ );
    $data =~ s/(\&|\<|\>|\"|\')/$HTML_ENCODE_MAP{$1}/sg;
    $data =~ s/ /\&nbsp;/g;
    $data =~ s/(\r|\n)/<br>\n/g;
    return $data;
}

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

sub kill_upcp {
    my $pid = shift or die;

    $logger->info("Sending kill signal to process group for $pid");
    kill -1, $pid;    # Kill the process group

    for ( 1 .. 60 ) {
        $logger->info("Waiting for processes to die");
        waitpid( $pid, POSIX::WNOHANG() );
        last if ( !kill( 0, $pid ) );
        sleep 1;
    }

    if ( kill( 0, $pid ) ) {
        $logger->info("Could not kill upcp nicely. Doing kill -9 $pid");
        kill 9, $pid;
    }
    else {
        $logger->info("Done!");
    }
}

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

sub setupenv {
    Cpanel::Env::cleanenv();
    delete $ENV{'DOCUMENT_ROOT'};
    delete $ENV{'SERVER_SOFTWARE'};
    if ( $ENV{'WHM50'} || $ENV{'NOWHOSTMRRES'} ) {
        $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 setup_and_clean_updatelogs {
    if ( !-d '/var/cpanel/updatelogs' ) {
        unlink('/var/cpanel/updatelogs');
        mkdir( '/var/cpanel/updatelogs', 0700 );
    }
    opendir( ULS, '/var/cpanel/updatelogs' );
    while ( my $file = readdir(ULS) ) {
        if ( -f '/var/cpanel/updatelogs/' . $file
            && ( stat( '/var/cpanel/updatelogs/' . $file ) )[9] < ( $now - ( 86400 * 10 ) ) ) {
            unlink("/var/cpanel/updatelogs/${file}");
        }
    }
    closedir(ULS);
}

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 ( -t STDOUT ) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return 'Terminal detected';
    }

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

    if ( $ENV{'NOWHOSTMRRES'} ) {
        $ENV{'CPANEL_IS_CRON'} = 0;
        return 'Called from whostmgr UI';
    }

    # 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 monitor_upcp {
    my $updatepid = shift or die;
    my $logger = shift;

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

    $logger->close_log;

    local $SIG{INT} = sub {
        $logger->error('User hit ^C');
        if ( -f $upcp_disallowed_path ) {
            $logger->error('Not allowing upcp slave to be killed during updatenow, just killing master');
            exit;
        }
        $logger->error('killing upcp');

        kill_upcp($updatepid);
        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 = new IO::Handle;
        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 '' );

            if ($usejs2) {
                if ( $line =~ s/\[.*\]\s+updateParent_(.*)_(.*)_(.*)_js.*\n//g ) {
                    my ( $from, $status, $cmd ) = ( $1, $2, $3 );
                    print "\n<script>parent.update_ui_status(\"[$from] $status : $cmd \")</script>\n";
                }
                if ( $line =~ s/\[.*\]\s+(\d+)\%\scomplete.*\n//g ) {
                    my $per = $1;
                    print "<script>parent.update_percent($per)</script>\n";
                    if ( $per >= 100 ) {
                        print "<script>finish_up()</script>\n";
                    }
                }
                $line = cleanse($line);
            }

            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;
}