MOON
Server: Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4
System: Linux vps.panamaemb.org.sg 3.10.0-1160.80.1.vz7.191.4 #1 SMP Thu Dec 15 20:31:06 MSK 2022 x86_64
User: panama (500)
PHP: 5.2.17
Disabled: NONE
Upload Files
File: //proc/3/root/installd/install
#!/usr/bin/perl
# installd - install                              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

# Helper routines for the log.
our $message_caller_depth = 1;
sub DEBUG($) { _MSG( 'DEBUG', "  " . shift ) }     # space pad debug messages.
sub ERROR($) { _MSG( 'ERROR', shift ) }
sub WARN($)  { _MSG( 'WARN',  shift ) }
sub INFO($)  { _MSG( 'INFO',  shift ) }
sub FATAL($) { _MSG( 'FATAL', shift ); exit 1; }

use strict;
use warnings;
use POSIX;
use Sys::Hostname ();
use IO::Handle    ();
use IO::Select    ();
use IPC::Open3    ();
use Cwd           ();

$ENV{'CPANEL_BASE_INSTALL'} = 1;
$ENV{'LANG'}                = 'C';
my $yumcheck  = 0;
my $lock_file = '/root/installer.lock';
$| = 1;
umask 022;

if ( open my $fh, '<', $lock_file ) {
    print "Detected an installer lock file ($lock_file).\n";
    print "Please be sure that an installer is not already running.\n\n";
    print "You can remove this file and re-run the cpanel install once you're certain another installation isn't in progress\n\n";
    my $pid = <$fh>;
    chomp $pid;
    print `ps -auxwww |grep $pid`;
    exit 1;
}
else {

    # Create the lock file.
    touch( $lock_file, $$ );
}

# Cleanup the lock file on exit.
END {
    if ( open my $fh, '<', $lock_file ) {
        my $pid = <$fh>;
        chomp $pid;
        close $fh;

        if ( $pid == $$ ) {
            print "Removing $lock_file\n";
            unlink $lock_file;
        }
    }
}

# Open the install logs for append.
my $installstart = open_logs();

# Determine local distro and version. Fail if unsupported.
my ( $distro, $distro_version ) = check_system_support();

# Validate hostname is FQDN.
check_hostname();

# Which wget?
my ( $wget_bin, $wget_args ) = get_download_tool_binary( $distro, $distro_version );

# Assure minimum setup.
bootstrap( $distro, $distro_version );

# Setup cpanel.config. This has to be done early for blocker checking.
setup_cpanel_config();

# Look for conditions that require tier manipulation or require us to block the install.
check_for_install_version_blockers( $distro, $distro_version );

DEBUG "Parsing command line arguments";
get_install_type(@ARGV);    # Set DNSONLY if need be.
my $skip_apache = ( grep( /^--skipapache/, @ARGV ) or -e '/root/skipapache' ) ? 1 : 0;

# Bootstrap checks.
INFO "Running health checks prior to start";
check_resolv_conf();
if ( $distro !~ m/bsd/i ) {
    check_update_system();
}
check_if_we_can_get_to_httpupdate();

my $installer_dir = Cwd::getcwd();

# Make sure the OS is relativley clean.
clean_install_check();

# TODO: Get rid of these files and replace them with /var/cpanel/dnsonly
# Disable services by touching files.
touch('/etc/entropychatdisable');
if ( is_dnsonly() ) {
    my @dnsonlydisable = qw( cppop cpdavd melange interchange );
    foreach my $dis_service (@dnsonlydisable) {
        ssystem( 'touch', '/etc/' . $dis_service . 'disable' );
    }
}

# Set selinux to permissive mode for installation.
if ( -e '/usr/sbin/setenforce' ) {
    ssystem( '/usr/sbin/setenforce', '0' );
}

# Install cpanel files and directories
install_cpanel_files();

# Stop and possibly remove some services
disable_and_remove_init_services();

# Init the package management system, update installed, then run sysup
ensure_rpms_installed( $distro, $distro_version );

# Remove rpms and stop unneeded services
disable_software($distro);

# Now software is installed, call rdate in case it couldn't be called earlier.
update_system_clock();

# Setup default cPanel users;
setup_cpanel_system_users();

# Re-compile and install perl if /usr/bin/perl is not > 5.8.8 at this point
ensure_perl_588($distro);

# Update/Install CPAN modules required by cPanel.
check_perl_modules();

# Update license information ASAP so code can get at it.
INFO "Updating license information";
ssystem(qw{/usr/local/cpanel/cpkeyclt});

# Upgrade to cloud linux if licensed via cpanel. Change distro if it updates.
( $distro, $distro_version ) = upgrade_to_cloud_linux( $distro, $distro_version );

################################################################
#
#    At this point, cpanel code and perl are stable            #
#
################################################################

if ( -e '/usr/sbin/pwconv' ) {
    INFO "making sure we are shadowed";
    ssystem('/usr/sbin/pwconv');
}

INFO "Making sure cPanel processes are not running";
foreach my $app (qw(cpsrvd cpdavd cphulkd)) {
    ssystem( '/usr/local/cpanel/etc/init/stop' . $app );
}

INFO("Setting up sysinfo.conf");
ssystem('/usr/local/cpanel/scripts/gensysinfo');

INFO("Setting up locale databases");
ssystem('/usr/local/cpanel/bin/build_locale_databases');

INFO("Making sure the firewall (if present) is setup for cPanel.");
ssystem('/usr/local/cpanel/scripts/configure_rh_firewall_for_cpanel');

INFO("Setting up secure /tmp if possible");
install_secure_tmp();

INFO("Making sure the 3rd party services setup in cpanel.conf are properly enabled");
setup_third_party_services();

INFO("Attending to Easy apache");
install_apache();

setup_misc_cpanel_config_files($distro);

# TODO: Move into rpm update sub and hunt down FB case about yum upgrade breaking the service.
os_service_start('syslog');

INFO("Setting the PHP binary used by /usr/local/cpanel/cpanel to the php shipped with cPanel & WHM");
touch('/var/cpanel/usecpphp');

INFO "Base Install Complete";
INFO "Starting final installation phase";

# Run upcp for the first time.
INFO " ";
INFO " ";
INFO "Running upcp for the first time.";
INFO " ";
INFO " ";
ssystem(qw{/usr/local/cpanel/scripts/upcp --force});

# Enable services for startup.
# TODO: Add chkconfig to RPM spec entries and remove this code.
if ( $distro !~ m/bsd/i ) {
    my @services = qw/cpanel mysql sshd/;
    INFO 'Adding services to chkconfig.';
    foreach my $service (@services) {
        INFO " - Enabling $service";
        ssystem( '/sbin/chkconfig', '--level', '35', $service, 'on' );
    }
}

schdir($installer_dir);

# Restore any staged cpanel accounts
cpanel_account_restore();

# enable cphulkd by default
enable_cphulkd();

my $finishtime        = time();
my $installtime       = $finishtime - $installstart;
my $installfinishtime = localtime($finishtime);

INFO sprintf( "cPanel install finished in %d minutes and %d seconds!", int( $installtime / 60 ), $installtime % 60 );

# Check if the kernel set for boot matches what's currently running (uname -r)
notify_if_boot_kernel_changed($distro);

close LOG;

# TODO: MARKER FOR END OF PROGRAM EXIT HERE
exit;

sub yum_nohang_ssystem {
    if ( !-e '/var/cpanel/useyum' ) {
        goto &ssystem;
    }

    my @cmd = @_;
    $yumcheck = 1;
    my $failcount = 0;
    my $result    = 1;
    while ($result) {    # While yum is failing.
        $result = ssystem(@cmd);
        last if ( !$result );    # yum came back clean. Stop re-trying

        $failcount++;
        if ( $failcount > 5 ) {
            FATAL "yum failed $failcount times.  Cannot continue!";
        }
    }
    $yumcheck = 0;
}

sub ssystem {
    my @cmd = @_;
    my $conf_hr = ref( $cmd[-1] ) eq 'HASH' ? pop(@cmd) : {};

    local $message_caller_depth = $message_caller_depth + 1;    # Set caller depth deeper during this sub so debugging it clearer.
    DEBUG '- ssystem [BEGIN]: ' . join( ' ', @cmd );
    open( RNULL, '<', '/dev/null' );
    my $io = new IO::Handle;
    my $pid = IPC::Open3::open3( "<&RNULL", $io, $io, @cmd );
    $io->blocking(0);

    my $select = IO::Select->new($io);

    my $exit_status;
    my $buffer                 = '';
    my $buffered_waiting_count = 0;
    while ( !defined $exit_status ) {
        while ( my $line = readline($io) ) {

            # Push the buffer lacking a newline onto the front of this.
            if ($buffer) {
                $line   = $buffer . $line;
                $buffer = '';
            }

            $line =~ s/\r//msg;    # Strip ^M from output for better log output.

            if ( $yumcheck && $line =~ /yum might be hung/ ) {
                kill 15, $pid;
                sleep 2;
                WARN "....yum is hung, trying to restart it....";
                ssystem(qw/killall -TERM yum/);
                sleep(20);
                ssystem(qw/killall -TERM yum/);
            }

            # Internally buffer on newlines.
            if ( $line =~ m/\n$/ms ) {
                DEBUG( "  " . $line );
                $buffered_waiting_count = 0;
            }
            else {
                print "." if ( $buffered_waiting_count++ > 1 );
                $buffer = $line;
            }
        }

        # Parse exit status or yield time to the CPU.
        if ( waitpid( $pid, 1 ) == $pid ) {
            $exit_status = $? >> 8;
        }
        else {

            # Watch the file handle for output.
            $select->can_read(0.1);
        }
    }
    ERROR "  - ssystem [EXIT_CODE] '$cmd[0]' exited with $exit_status (ignored)" if ($exit_status);

    close(RNULL);
    $io->close();

    DEBUG '- ssystem [END]';

    return $exit_status;
}

sub schdir {
    my $dir = shift;
    my $cwd = Cwd::getcwd();
    chdir($dir) || die "Cannot chdir to ${dir}, cwd was: $cwd";
}

# TODO: This sub is no longer used. We believe it was an issue
#       pre- Centos 4 and so no longer an issue for the installer
sub check_for_rpm_race_condition {

    DEBUG 'Verifying we do not have a RPM race condition';
    if ( -e '/var/lib/rpm/__db.001' ) {
        my $runrpm  = 0;
        my $isfping = `ps uxawwwwwww`;
        my @ISFPING = split( /\n/, $isfping );
        foreach (@ISFPING) {
            if (/rpm/) {
                $runrpm = 1;
            }
        }
        if ( !$runrpm ) {
            WARN "detected running rpm process";
            ssystem('rm -f /var/lib/rpm/__db.*');
        }
    }
}

sub is_dnsonly {
    return -e '/var/cpanel/dnsonly' ? 1 : 0;
}

sub get_install_type {
    my @args = @_;

    # TYPE could be DNSONLY
    my $type = 'standard';
    if (@args) {
        foreach my $val (@args) {
            next if $val =~ m/^--/;
            $type = $val;
            last;
        }
    }

    if ( $type =~ m/dnsonly/i ) {
        INFO "DNSONLY install requested";
        touch('/var/cpanel/dnsonly');
    }

    INFO "Install type: $type\n";
}

sub touch {
    my $file = shift;

    open( my $fh, ">>", $file ) or return;
    foreach my $line (@_) {    # concat anything found.
        print {$fh} $line;
    }
    close $fh;
}

sub check_system_support {
    my $system = ( POSIX::uname() )[0];

    if ( $system =~ m/freebsd/i ) {
        my $machine = ( POSIX::uname() )[3];
        my ($version) = $machine =~ m/freebsd\s+(\d+\.\d+)/i or invalid_system("Cannot determine the distro number for FreeBSD");
        invalid_system() if ( $version < 7.29999 );    # Safety for floating point check.

        INFO "FreeBSD version $version detected!";
        return ( $system, $version );
    }
    elsif ( $system =~ m/linux/i ) {

        # /etc/redhat-release must be present.
        my $rhel_release = '/etc/redhat-release';
        ( -e $rhel_release ) or invalid_system("I could not detect '$rhel_release'.");

        # rpm must manage it.
        my $release_rpm = `rpm -qf $rhel_release`;
        chomp $release_rpm;
        $release_rpm or invalid_system("'$rhel_release' is not managed by RPM.");

        # an rpm we recognize must manage it.
        my ( $distro, $distro_version ) = $release_rpm =~ m{^(\D+)-(\d+).*$}ms;
        $distro_version = $distro_version + 0;
        $distro_version or invalid_system("Unexpected RPM '$release_rpm' found managing the file $rhel_release.");

        # That RPM must have redhat or centos in the name.
        $distro =~ m/centos|redhat|enterprise-release|cloud/i or invalid_system("Unexpected rpm '$distro' found managing the file '$rhel_release'");
        $distro =~ s/-release//imsg;
        if ( $distro eq 'enterprise' ) {
            $distro = 'redhat';
        }

        INFO "$distro $distro_version (Linux) detected!";

        # The version number must be 4-6
        ( $distro_version <= 6 && $distro_version >= 4 ) or invalid_system("$distro version $distro_version is unsupported by cPanel for new installs.");

        # Supported distros for installer: redhat/red hat enterprise/cloud/centos
        $distro = ( $distro =~ m/redhat|hat enterprise/i ) ? 'redhat' : ( $distro =~ m/cloud/i ) ? 'cloud' : 'centos';
        return ( $distro, $distro_version );
    }

    invalid_system("Unknown or unsupport operating system. ($system)");
    exit(2);
}

sub invalid_system {
    my $message = shift || '';
    chomp $message;
    ERROR "$message";
    ERROR "Unsupported distro detected. cPanel only supports CentOS or RedHat Enterprise 4-6 and upstream supported Free BSD systems >= 7.3";
    FATAL "Please re-install cPanel from a valid distro";
}

sub check_hostname {
    my $hostname = Sys::Hostname::hostname();
    if ( $hostname !~ m/\./ ) {
        $hostname = `hostname -f`;
        chomp $hostname;
    }
    INFO "Validating that '$hostname' is a FQDN";
    if ( $hostname =~ /^www\./ ) {
        FATAL "Hostname $hostname detected. Hostnames cannot start with www! Please change it.";
    }

    if ( $hostname !~ /\./ ) {
        ERROR "";
        ERROR "********************* ERROR *********************";
        ERROR "";
        ERROR "Your hostname ($hostname) is not set properly. Please";
        ERROR "change your hostname to a fully qualified domain name,";
        ERROR "and re-run this installer.";
        ERROR "";
        ERROR "********************* ERROR *********************";
        FATAL "exiting...";
    }
}

sub clean_install_check {
    INFO "cPanel Layer 1 Installer Starting.....";
    INFO "Warning !!! Warning !!! WARNING !!! Warning !!! Warning";
    INFO "-------------------------------------------------------";
    INFO "cPanel requires a fresh/clean server!";
    INFO "If you are serving websites off this server (and are";
    INFO "not already running cPanel) this installer will";
    INFO "overwrite all of your config files.  You should hit";
    INFO "Ctrl+C NOW!!!";
    INFO "If this is a new server please ignore this messages";
    INFO "-------------------------------------------------------";
    INFO "Warning !!! Warning !!! WARNING !!! Warning !!! Warning";
    INFO "Waiting 5 seconds.....";
    INFO "";
    INFO "";
    sleep(5);

    # TODO: We should die if we see evidence of any control panel.
    INFO 'Checking for another control panel...';
    my @server_detected;
    push @server_detected, 'DirectAdmin' if ( -e '/usr/local/directadmin' );
    push @server_detected, 'Plesk'       if ( -e '/etc/psa' );
    push @server_detected, 'Ensim'       if ( -e '/etc/appliance' || -d '/etc/virtualhosting' );

    #push @server_detected, 'Alabanza'    if ( -e '/etc/mail/mailertable' );
    push @server_detected, 'Zervex' if ( -e '/var/db/dsm' );
    push @server_detected, 'Web Server Director' if ( -e '/bin/rpm' && `/bin/rpm -q ServerDirector` =~ /^ServerDirector/ms );

    return if ( !@server_detected );

    ERROR "Evidence that the following servers have been installed on this system was detected";
    ERROR $_ foreach (@server_detected);
    FATAL 'cPanel needs to be installed on a clean server';
}

sub open_logs {
    my $installstart = time();

    my $log_file = '/var/log/cpanel-install.log';
    if ( my $mtime = ( stat($log_file) )[9] ) {
        my $bu_file = $log_file . '.' . $mtime;
        ssystem( 'cp', $log_file, $bu_file ) unless -e $bu_file;
    }
    open( LOG, '>>', $log_file );
    select(LOG);
    $| = 1;

    select(STDOUT);

    my $installstarttime = localtime($installstart);
    INFO "cPanel install started at: ${installstarttime}!";
    INFO "This install will take 10-70 minutes depending on your hardware.";
    INFO "Now is the time to go get another cup of coffee/jolt.";
    INFO "The install will log to /var/log/cpanel-install.log.";
    INFO "";
    INFO "Beginning Installation";
    return $installstart;
}

sub disable_and_remove_init_services {
    INFO "Disabling unneeded services...";
    ssystem('/usr/local/cpanel/scripts/disable_unused_xinetd');

    my @service_shutdown = qw(named);
    push @service_shutdown, qw(exim smail sendmail postfix master mysql mysqld httpd apache wu-ftpd inetd ncsd)
      if ( !is_dnsonly() );

    # Disable and remove the ncsd service.
    if ( -e '/etc/init.d/nscd' ) {
        push @service_shutdown, 'ncsd';
        ssystem( '/sbin/chkconfig', '--del', 'nscd' );
    }

    INFO "Stopping services:";
    foreach my $service (@service_shutdown) {
        os_service_stop($service);
    }
}

sub ensure_rpms_installed {
    my ( $distro, $distro_version ) = @_;

    if ( $distro =~ m/bsd/i ) {
        install_bsd_software();
        return;
    }

    # Disable rpmforge repos
    if ( glob '/etc/yum.repos.d/*rpmforge*' ) {
        WARN 'DISABLING rpmforge yum repos';
        mkdir( '/etc/yum.repos.d.disabled', 0755 );
        ssystem('mv -fv -- /etc/yum.repos.d/*rpmforge* /etc/yum.repos.d.disabled/ 2>/dev/null');
    }

    # Install fastest mirror plugin for CentOS
    if ( $distro =~ m/centos/ ) {
        my $valid = `$wget_bin $wget_args - http://www.cpanel.net/apps/proxytest/proxytest.cgi`;

        if ( $valid =~ m/^0/ ) {
            INFO "No proxy found, installing fastest mirror plugin.";
            ssystem 'yum', 'clean', 'all';
            ssystem 'yum', '-y',    'install', ( $distro_version =~ /^4/ ? 'yum-plugin-fastestmirror' : 'yum-fastestmirror' );
            ssystem 'yum', 'clean', 'all';
        }
        else {
            WARN "http proxy found. Skipping fastest mirror plugin for CentOS";
        }

        # Lower the number of threads for the fastestmirror plugin on CentOS 6
        if ( $distro_version == 6 && open( my $fh, '<', '/etc/yum/pluginconf.d/fastestmirror.conf' ) ) {
            my @conf = <$fh>;
            close $fh;

            if ( open( my $ofh, '>', '/etc/yum/pluginconf.d/fastestmirror.conf' ) ) {
                foreach my $line (@conf) {
                    $line =~ s/^(\s*maxthreads\s*=\s*)\d+/${1}1/;
                    print {$ofh} $line;
                }

            }
            else {
                WARN("Could not update fastest mirror plugin for yum to lower it's threads");
            }

        }
    }

    # This if/else loop to make sure yum/up2date are setup. Do a little boostrapping with yum if possible.
    if ( -e '/etc/yum.conf' || -e '/var/cpanel/useyum' ) {    # YUM
                                                              # Minimal packages needed to use yum.

        # Install perl-devel on centhat 6
        if ( $distro_version == 6 ) {
            INFO("Installing perl development packages so that system perl can install from CPAN");
            yum_nohang_ssystem( '/usr/bin/yum', '-y', 'install', qw{ perl-devel perl-CPAN perl-Module-Build perl-core perl-libwww-perl crontabs sysstat} );
        }

        # Remove all excludes from /etc/yum.conf
        ssystem( '/usr/local/cpanel/scripts/checkyum', '--nokernel', '--noperl' );
        ssystem( 'touch', '/etc/checkyumdisable' );           # Disable checkyum

        yum_nohang_ssystem( '/usr/bin/yum', '-y', 'install', 'kernel-headers' );    # Needed because Cpanel::SysPkgs excludes kernel_version

        # Make sure all rpms are up to date.
        yum_nohang_ssystem( '/usr/bin/yum', '-y', 'update' );

        # Reinstate yum exclusions
        unlink '/etc/checkyumdisable';
        ssystem('/usr/local/cpanel/scripts/checkyum');
    }
    elsif ( -e '/var/cpanel/use2update' || -e '/usr/sbin/up2date' ) {               # RHEL 4
        ssystem('/usr/local/cpanel/scripts/checkup2date');
    }
    else {
        invalid_system("Could not determine the correct rpm update system. It should be yum or up2date");
    }

    # Check to make sure all installed rpms are up to date
    # TODO parse this output and determine if our mirror access is broken. Die on failure.
    ssystem('/usr/local/cpanel/scripts/rpmup');

    # Make sure all of the rpms cPanel needs are installed
    ssystem('/usr/local/cpanel/scripts/sysup');

    # TODO: Remove this if RHEL 4-6 don't have this version after yum -y update
    # Remove fontconfig-2.2.3-7 if present. It breaks stuff when we try to build.
    ssystem('/usr/local/cpanel/scripts/fontconfigfix');

    if ( !-e '/usr/bin/gcc' ) {
        ERROR "Could not automatically install gcc. The GNU C compiler is required for a successful cPanel installation.";
        ERROR "gcc often fails to install due to missing dependencies such as a required update of the kernel-headers.";
        ERROR "cPanel specifically excludes kernel updates and this may have prevented the gcc installation.";
        ERROR "The installer will attempt once more to install gcc by allowing updates of the system kernel.";
        FATAL "exiting...";    # Terminal Bell
    }
}

sub disable_software {
    my $distro = shift or die;
    return if ( is_dnsonly() or $distro =~ m/bsd/i );

    my @remove_rpms = qw(
      mysql
      MySQL
      mysql-max
      MySQL-Max
      mysql-devel
      MySQL-devel
      mysql-client
      MySQL-client
      mysql-ndb-storage
      MySQL-ndb-storage
      mysql-ndb-management
      MySQL-ndb-management
      mysql-ndb-tools
      MySQL-ndb-tools
      mysql-ndb-extra
      MySQL-ndb-extra
      mysql-shared
      MySQL-shared
      mysql-libs
      MySQL-libs
      mysql-bench
      MySQL-bench
      mysql-server
      MySQL-server
      wu-ftpd
      postfix
      sendmail
      smail
      spamassassin
      apache-conf
      mod_perl
    );

    INFO 'Ensuring conflicting services are uninstalled.';
    foreach my $rpm (@remove_rpms) {
        my $installed = `rpm -q $rpm`;
        next if ( $installed =~ m/is not installed/ms );

        DEBUG " Removing $rpm";
        ssystem( 'rpm', '-e', '--nodeps', $rpm );
    }

    INFO 'Ensuring conflicting service references are removed from rpm database (but leaving service installed).';
    my @all_pkgs = `rpm -qa --queryformat '%{name}\n'`;

    return if ($skip_apache);

    # TODO: Why are we doing --justdb??? Fix this after at least 11.30
    foreach my $rpm ( grep m/http|php|apache|mod_perl/, @all_pkgs ) {
        chomp $rpm;
        DEBUG " Removing $rpm\n";
        ssystem( 'rpm', '-e', '--justdb', '--nodeps', $rpm );
    }
}

sub setup_cpanel_system_users {
    INFO 'Creating default cPanel users.';
    my @cpanel_users = qw( cpanel );
    if ( !is_dnsonly() ) {
        push @cpanel_users, qw( cpanelhorde cpanelphpmyadmin cpanelphppgadmin cpanelroundcube );
    }

    foreach my $cpanel_user (@cpanel_users) {
        next if ( getpwnam($cpanel_user) );
        DEBUG " adding  $cpanel_user ...";
        ssystem( '/usr/local/cpanel/scripts/adduser', '--nochecks', '--noshell', $cpanel_user, '/var/cpanel/userhomes' );
    }

    if ( !is_dnsonly() && !getpwnam('mailman') ) {    # Fix for broken mailman installer. TODO: Still needed?
        DEBUG " adding  mailman ...";
        ssystem( '/usr/local/cpanel/scripts/adduser', '--nochecks', '--noshell', 'mailman', '/usr/local/cpanel/3rdparty/mailman' );
    }
}

sub setup_misc_cpanel_config_files {
    my $distro = shift or die;
    INFO 'Setting up misc cPanel config files.';

    # FB Case about running this out of maintenance and into the install script
    DEBUG '  Running scripts/secureit';
    ssystem( '/usr/local/cpanel/scripts/secureit', '--fast' );

    # TODO: Should/Can we remove these installations? Open FB over it.
    DEBUG '  Installing issue and bashrc';
    ssystem( 'cp', '-f', $installer_dir . '/issue',     '/etc' );
    ssystem( 'cp', '-f', $installer_dir . '/issue.net', '/etc' );
    ssystem( 'cp', '-f', $installer_dir . '/bashrc',    '/etc' );
    chmod( 0755, '/etc/bashrc' );

    # sync time on reboot.
    my $rc_local = ( $distro =~ m/bsd/i ) ? '/etc/rc.local' : '/etc/rc.d/rc.local';
    DEBUG '  Configuring rdate to run on reboot';
    if ( open my $rc_fh, '>>', $rc_local ) {
        print {$rc_fh} "/scripts/rdate &\n";
        close $rc_fh;
    }

    DEBUG '  Setting whemtheme to x';
    open( THEME, '>', '/var/cpanel/whmtheme' );
    print THEME 'x';
    close(THEME);
}

sub install_apache {
    return if ( is_dnsonly() );
    if ($skip_apache) {
        WARN "Skipping Apache installation due to command line request";
        return;
    }

    INFO "Installing Apache...";

    DEBUG "Moving /etc/httpd out of the way and pointing /etc/httpd to /usr/local/apache";
    if ( -e '/etc/httpd' && !-e '/usr/local/apache' ) {
        if ( !-l '/etc/httpd' && !-e '/etc/httpd.old' ) {
            ssystem( 'mv', '/etc/httpd', '/etc/httpd.old' );
        }
        else {
            unlink '/etc/httpd';
        }
    }
    ssystem(qw{ln -sf /usr/local/apache /etc/httpd});

    DEBUG "Determining if a custom profile needs to be used";
    my $local_profile = '/etc/cp_easyapache_profile.yaml';
    my $ea_profile = ( -s $local_profile ) ? $local_profile : 'cpanel_default';

    # install EA
    DEBUG "Running apache install scripts";
    ssystem( '/usr/local/cpanel/scripts/cpanel_easy_sanity_check', '--quiet' );
    ssystem( '/usr/local/cpanel/scripts/easyapache', "--profile=$ea_profile", '--build' );

    DEBUG "Symlink etc -> conf as long as conf isn't a symlink";
    if ( -d '/usr/local/apache/conf' && !-l '/usr/local/apache/conf' ) {
        symlink(qw{conf /usr/local/apache/etc});
    }
}

sub install_cpanel_files {

    # Install cPanel files.
    INFO 'Installing /usr/local/cpanel files.';
    unlink 'updatenow.static';

    my $update_source = get_update_source();
    DEBUG "HTTPUPDATE is set to $update_source";

    if ( -e '/scripts' && !-l '/scripts' ) {
        if ( !-d '/scripts' ) {
            WARN "/scripts detected as a file. Moving it out of the way";
            ssystem( qw{/bin/mv /scripts}, "/scripts.o.$$" );
        }
        else {
            WARN "/scripts detected. moving contents to /usr/local/cpanel/scripts";
            ssystem(qw{mkdir -p /usr/local/cpanel/scripts});
            ssystem('cd / && tar -cf - scripts | (cd /usr/local/cpanel && tar -xvf -)');
            ssystem(qw{/bin/rm -rf /scripts});
        }
    }
    unlink '/scripts';
    symlink(qw{/usr/local/cpanel/scripts /scripts}) if ( !-e '/scripts' );
    WARN("/scripts should be a symlink to /usr/local/cpanel/scripts. cPanel doesn't use /scripts any more but you might")
      if ( !-l '/scripts' );

    # Download the tar.gz files and extract them instead.
    my $source = "http://$update_source/cpanelsync/installer/cpanel/scripts/updatenow.static.bz2";
    DEBUG "Retrieving updatenow.static from $source";
    cpfetch($source);
    ssystem( "/usr/bin/bunzip2", "updatenow.static.bz2" );
    chmod 0755, 'updatenow.static';
    my $exit;
    for ( 1 .. 5 ) {    # Re-try updatenow if it fails.
        $exit = ssystem( './updatenow.static', '--upcp', '--no-check-perl', '--force' );
        last if ( $exit == 0 );
        DEBUG("Detected a failed sync. Re-trying updatenow.static to correct");
    }
    if ($exit) {
        FATAL("Unable to sync cpanel. Please verify your network connectivity to httpupdate.cpanel.net and re-run the installer");
    }
}

sub ensure_perl_588 {
    my $distro = shift or die;

    INFO "Ensuring /usr/bin/perl is at least 5.8.8";
    my $has_perlver = `/usr/bin/perl -e'print \$]'`;
    chomp $has_perlver;

    if ( $distro =~ m/bsd/i ) {
        INFO "Perl ($has_perlver) will not be updated on BSD";
        return;
    }

    DEBUG "/usr/bin/perl = $has_perlver";

    INFO "Determining system VPS type";
    my $envtype = `/usr/local/cpanel/bin/envtype` || '';
    chomp $envtype;
    open( my $envtype_fh, '>', '/var/cpanel/envtype' );
    print $envtype_fh $envtype;
    close($envtype_fh);

    my $isvps = ( $envtype ne 'standard' ) ? 1 : 0;
    INFO "Detected a '$envtype' VPS" if ($isvps);

    # These 2 vars need to stay in sync.
    my $needed_perl    = '5.008008';
    my $perl_installer = 'perl588installer';

    # Custom compile perl if /usr/bin/perl < $needed_perl (5.8.8)
    unless ( $isvps or $has_perlver < $needed_perl ) {
        INFO "Perl does not need to be updated";
        return;
    }

    INFO 'Updating Perl.';

    #child
    local $0 = 'cPanel install - compiling perl';
    mkdir '/root/.gnupg', 0700 unless -e '/root/.gnupg';
    schdir($installer_dir);
    if ( -e "/root/$perl_installer.tar.gz" ) {
        require File::Copy;
        WARN "Using local copy of $perl_installer.tar.gz";
        File::Copy::copy( "/root/$perl_installer.tar.gz", "$perl_installer.tar.gz" );
    }
    else {
        cpfetch("$perl_installer.tar.gz");
    }
    if ( !-e "$perl_installer.tar.gz" ) {
        ERROR "Unable to download $perl_installer.tar.gz!";
        return;
    }

    INFO "extracting perl";
    ssystem( 'tar', '-xzv', '--no-same-owner', '--no-same-permissions', '-f', "$perl_installer.tar.gz" );

    INFO "Compiling perl";
    schdir($perl_installer);
    chmod 0644, '/usr/local/cpanel/scripts/checkperlmodules', '/usr/local/cpanel/bin/rrdtoolcheck';    # Disable rrdtool/chkperl in the perl installer

    ssystem( './install', '-optimize-memory', '--nomodules' );

    chmod 0755, '/usr/local/cpanel/scripts/checkperlmodules', '/usr/local/cpanel/bin/rrdtoolcheck';    # Re-enable rrdtool/chkperl
    schdir($installer_dir);

    INFO "Perl install complete";
    return;
}

sub check_perl_modules {

    # Cleanup .cpan dir
    INFO 'Cleaning up CPAN cache.';
    ssystem( 'rm', '-rf', '/root/.cpan' );

    # Make sure cpan downloads a new list of modules
    unlink('/home/.cpcpan/modulecheck');

    # Assure spamd is shut down.
    ssystem( 'killall', '-TERM', 'spamd' );

    INFO "Bootstrapping CPAN. (CPAN, IO::Tty, Expect, YAML::Syck, Bundle::LWP)";
    ssystem( '/usr/local/cpanel/scripts/perlinstaller', 'CPAN' );
    ssystem( '/usr/local/cpanel/scripts/perlinstaller', 'IO::Tty', 'Expect', 'YAML::Syck', 'Bundle::LWP' );

    # checkperlmodules tries to do these one at a time. This approach is a little cheaper when in installing many modules at once.
    INFO "Attempting a quick install of most of the needed CPAN modules prior to calling checkperlmodules";
    my @check_perl_modules = qw {
      Locales version CDB_File Module::Build BSD::Resource Class::Std Digest::MD5::File Expect Encode::Guess Encode::MIME::Name Encode::Detect::Detector
      Data::Dump File::Copy::Recursive File::ReadBackwards File::Find::Rule IO::Tty Sys::Hostname::Long local::lib AppConfig Template YAML::Syck JSON::Syck
      cPanel::MemTest List::MoreUtils DateTime::Locale DateTime DB_File HTTP::Date Scalar::Util MIME::Base64 URI Net::FTP HTML::Tagset HTML::Parser
      HTML::HeadParser LWP Bundle::LWP DBI Crypt::SSLeay CPAN::SQLite Data::Dumper Digest::MD5 Digest::SHA1 Encode
      ExtUtils::Constant ExtUtils::Install ExtUtils::ParseXS File::Touch Filesys::Df Filesys::Virtual Filter::Util::Call Getopt::Long Getopt::Param::Tiny
      Compress::Raw::Zlib Authen::Libwrap Net::FTPSSL Net::SSL Net::SSLeay IO::Compress::Gzip IO::Scalar IO::Socket::SSL IO::Stty IO::Uncompress::Gunzip
      Lchown List::Util MD5 Net::DNS Net::OSCAR Pod::Perldoc Storable Sys::Syslog Term::ReadKey Term::ReadLine::Perl Time::HiRes Tree::MultiNode Unix::PID
      Unix::PID::Tiny XML::LibXML::Common XML::LibXML XML::Parser XML::SAX XML::Simple lib::restrict Crypt::Passwd::XS Filesys::Statvfs Crypt::GPG
      Class::Accessor Class::Accessor::Fast File::MMagic::XS Email::Valid ExtUtils::MakeMaker Mail::SRS Acme::Spork Archive::Tar Archive::Tar::Streamed
      Archive::Zip MIME::Lite Business::OnlinePayment::AuthorizeNet Business::UPS CGI Class::Std::Utils Compress::Bzip2 Compress::Zlib DBIx::MyParsePP
      DBD::SQLite2 Date::Parse File::Tail GD::Graph GD::Text::Align Memoize Geo::IPfree HTTP::Daemon::App IO::Socket::ByteCounter Image::Size
      Mail::DomainKeys Error NetAddr::IP Net::DNS::Resolver::Programmable Mail::SPF Mail::SPF::Query Mail::DKIM IP::Country Graph::Easy Graph::Flowchart
      Mail::SpamAssassin URI::Escape File::Find::Rule::Filesys::Virtual File::Slurp Net::DAV::Server Net::Daemon Net::Daemon::SSL Net::LDAP
      Net::LDAP::Schema Net::LDAP::Server Net::IP::Match::Regexp OLE::Storage_Lite Parse::RecDescent Quota SVG::TT::Graph Safe::Hole Text::CSV
      Spreadsheet::ParseExcel Spreadsheet::WriteExcel String::CRC32 SQL::Statement Set::Crontab Tie::IxHash Tie::ShadowHash Tie::DBI URI::URL
      Bundle::Interchange Devel::PPPort Linux::Inotify2 };
    ssystem( '/usr/local/cpanel/scripts/perlinstaller', @check_perl_modules );

    # Make sure spamd is stopped during perl module installs.
    INFO "Updating all cpanel required CPAN modules.";
    for ( 1 .. 2 ) {
        INFO("Running checkperlmodules for time $_");
        ssystem( '/usr/local/cpanel/scripts/checkperlmodules', '--full', '--nowarn' );
    }

    # Make sure spamd is stopped during perl module installs.
    INFO "Making sure any library dependencies are met for perl modules.";
    ssystem('/usr/local/cpanel/install/perlmods');

    INFO "Making sure rrdtool is setup correctly";
    ssystem('/usr/local/cpanel/bin/rrdtoolcheck');

}

sub cpanel_account_restore {
    my $acct_restore_file = '/etc/cpanelacctrestore';
    return if ( !-e $acct_restore_file );

    INFO "Restoring Accounts.";
    sleep(2);

    open( my $fh, $acct_restore_file );
    while (<$fh>) {
        s/\n//g;
        DEBUG "Restoring $_";
        ssystem( "/usr/local/cpanel/scripts/restorepkg", "$_" );
    }
    close($fh);

    unlink($acct_restore_file);
}

sub install_secure_tmp {
    return if ( is_dnsonly() );

    INFO 'Securing the /tmp and /var/tmp directories.';
    ssystem( '/usr/local/cpanel/scripts/securetmp', '--auto', '--install' );

    # TODO: #################### Work around securetmp problem with no loopback ####################
    my $failed_securetmp = 0;
    if ( !-e '/tmp' ) {
        mkdir '/tmp';
    }

    my $mode = ( stat('/tmp') )[2] & 07777;
    if ( sprintf( '%o', $mode ) ne '1777' ) {
        $failed_securetmp = 1;
        chmod oct('1777'), '/tmp';
    }

    if ($failed_securetmp) {
        ERROR "Unable to secure /tmp directory";
        if ( open my $excl_fh, '>>', '/etc/cpanelsync.exclude' ) {
            print {$excl_fh} "\n/usr/local/cpanel/scripts/securetmp\n";
            close $excl_fh;
        }
        if ( open my $securetmp_fh, '>', '/usr/local/cpanel/scripts/securetmp' ) {
            print {$securetmp_fh} "#!/bin/sh\necho \"/usr/local/cpanel/scripts/securetmp is disabled\"\necho \"It is not compatible with this system. Check the kernel for loopback support.\"\n";
            close $securetmp_fh;
        }
        ssystem( 'mkdir', '-p', '/var/cpanel/version/' );
        ssystem( 'touch', '/var/cpanel/version/securetmp_disabled' );
        WARN "---- DISABLING securetmp on your system!!! ----";
    }
}

sub check_resolv_conf {
    use Socket;

    INFO "Validating that we can lookup domains";
    my @domains = qw(
      cpanel.net
      www.cpanel.net
      updates.cpanel.net
      httpupdate.cpanel.net
      layer2.cpanel.net
      layer1.cpanel.net
      www.google.com
    );

    foreach my $domain (@domains) {
        DEBUG "Testing $domain";
        next if ( gethostbyname($domain) );
        ERROR '!' x 105 . "\n";
        ERROR "Cannot resolve $domain. Please check /etc/resolv.conf. Installation terminated.\n";
        FATAL '!' x 105 . "\n";
    }
}

sub read_config {
    my $file = shift or die;
    my $config = {};

    open( my $fh, "<", $file ) or return $config;
    while ( my $line = readline $fh ) {
        chomp $line;
        if ( $line =~ m/^\s*(.*?)\s*=\s*(.*?)\s*$/ ) {
            my $key = $1 or next;    # Skip loading the key if it's undef or 0
            my $value = $2;
            $config->{$key} = defined $value ? $value : '';
        }
    }
    return $config;
}

sub save_config {
    my $file     = shift or die;
    my $settings = shift or die;

    INFO "Writing out $file";
    if ( open my $fh, '>', $file ) {
        foreach my $key ( sort keys %$settings ) {
            my $value = $settings->{$key};
            print {$fh} "$key=$value\n";
        }
        close $fh;
    }
    else {
        ERROR "Unable to write to $file: $!";
    }
}

sub setup_cpanel_config {
    my $cp_etc_path = '/usr/local/cpanel/etc';

    my $config_target  = '/var/cpanel/cpanel.config';
    my $config_default = $cp_etc_path . '/cpanel.config';
    my $custom_config  = '/root/cpanel_profile/cpanel.config';

    # Setup the the etc dir for downloading the default cpanel.config
    ssystem( 'mkdir', '-p', $cp_etc_path );
    chmod 0755, $cp_etc_path;

    # Assure /var/cpanel is there.
    mkdir '/var/cpanel';
    chmod 0755, '/var/cpanel';

    # Download the default cpanel.config if not already present.
    if ( !-e $config_default ) {
        INFO("Downloading default cpanel config");
        cpfetch( 'cpanelsync/installer/cpanel/etc/cpanel.config.bz2', "$config_default.bz2" );
        ssystem( "/usr/bin/bunzip2", "$config_default.bz2" );
        ( -e $config_default ) or die("Failed to download default cpanel.config file.");
        chmod 0644, $config_default;
    }

    # If no customization, just put the file in place and return.
    if ( !-e $config_target ) {
        INFO "Installing the default cPanel configuration file from $config_default";
        ERROR "$config_default could not be found!" if ( !-e $config_default );

        ssystem( qw{cp -fv}, $config_default, $config_target );

        # No further action if the file was just put in place and no custom config is found.
        return if ( !-e $custom_config );
    }

    # Load in whatever is in the current config file
    my $current_settings = read_config($config_target);

    # Merge in $custom_config, overwriting whatever is there.
    if ( -e $custom_config ) {
        INFO "Merging in custom settings from $custom_config";

        my $custom_settings = read_config($custom_config);
        foreach my $key ( sort keys %$custom_settings ) {
            my $value = $custom_settings->{$key};
            INFO " - Customizing $key=$value";
            $current_settings->{$key} = $value;
        }
    }

    # Pull any missing keys in the current config from $config_default
    my $default_settings = read_config($config_default);
    foreach my $key ( sort keys %$default_settings ) {
        next if ( exists $current_settings->{$key} );
        my $value = $default_settings->{$key};
        INFO "Injecting missing setting '$key=$value' into $config_target";
        $current_settings->{$key} = $value;
    }

    # Write out $config_target with our changes.
    save_config( $config_target, $current_settings );
}

sub setup_third_party_services {

    # Read in cpanel.config manually so we don't have to load modules.
    my $config = read_config('/var/cpanel/cpanel.config');

    # Setup the FTP server. Default to proftpd
    {
        my $target = $config->{'ftpserver'} || 'proftpd';
        $target = 'disabled' if ( is_dnsonly() );    # DNSONLY installs cannot set a custom ftp server.

        INFO "Setting up FTP server to '$target'";
        if ( $target !~ m/^(disabled|proftpd|pure-ftpd)$/ ) {
            WARN "$target is an unsupported ftpserver. Will default to 'proftpd' instead";
            $target = 'proftpd';
        }
        ssystem( '/usr/local/cpanel/scripts/setupftpserver', '--force', $target );
    }

    # Setup the name server. Default to bind
    {
        my $target = $config->{'local_nameserver_type'} || 'bind';
        INFO "Setting up name server to '$target'";
        if ( $target !~ m/^(disabled|bind|nsd)$/ ) {
            WARN "$target is an unsupported name server. Will default to 'bind' instead";
            $target = 'bind';
        }

        if ( $target eq 'bind' && $distro =~ m/centos|redhat/i && $distro_version >= 6 ) {
            if ( -e '/etc/named.conf' ) {
                INFO "Saving /etc/named.conf, and rebuild with cPanel defaults";
                if ( rename '/etc/named.conf', '/etc/named.conf.precpanelinstall' ) {
                    ssystem('/usr/local/cpanel/scripts/rebuilddnsconfig');
                }
                else {
                    WARN "Unable to rebuild /etc/named.conf file";
                }
            }
        }

        ssystem( '/usr/local/cpanel/scripts/setupnameserver', '--force', $target );
    }

    # Make sure exim is installed so setupmailserver doesn't freak out.
    INFO("Setting up exim");
    ssystem( '/usr/local/cpanel/scripts/eximup', '--force' );

    # Setup the mail server. Default to courier
    {
        my $target = $config->{'mailserver'} || 'courier';
        $target = 'disabled' if ( is_dnsonly() );    # DNSONLY installs cannot set a custom mail server.

        INFO "Setting up mail server to '$target'";
        if ( $target !~ m/^(disabled|courier|dovecot)$/ ) {
            WARN "$target is an unsupported mail server. Will default to 'courier' instead";
            $target = 'courier';
        }
        ssystem( '/usr/local/cpanel/scripts/setupmailserver', '--force', $target );
    }

    # Force update mysql just to be sure.
    ensure_mysql_enabled();
    INFO("Setting up mysql");
    ssystem( '/usr/local/cpanel/scripts/mysqlup', '--force' );
}

sub ensure_mysql_enabled {

    # stop gap measure until case 51994 is resolved.
    # mysql cannot be bootstrapped on BSD without it first
    # being enabled in /etc/rc.conf
    return unless ( $distro =~ m/bsd/i );
    if ( open( my $rc_conf_fh, '+<', '/etc/rc.conf' ) ) {
        while ( my $line = readline($rc_conf_fh) ) {
            if ( $line =~ m{^mysql_enable\s*=} ) {
                close $rc_conf_fh;
                return;
            }
        }
        INFO("Enabling mysql in rc.conf");
        print {$rc_conf_fh} qq{\nmysql_enable="YES"\n};
        close $rc_conf_fh;
    }
}

sub check_if_we_can_get_to_httpupdate {
    return if ( $wget_bin !~ m/wget/ );    # Just skip this check on BSD if no wget is avail.

    foreach my $src ( 'index.html', 'modules/index.html' ) {
        my $page = `$wget_bin $wget_args - http://httpupdate.cpanel.net/pub/CPAN/$src`;

        if ( $page =~ m/perl/i && $page =~ m/CPAN/ ) {
            INFO "we can connect to httpupdate.cpanel.net";
            return;
        }
    }
    FATAL "Cannot download from httpupdate.cpanel.net at the moment.";
}

# A tiny version of Cpanel::RpmUtils::checkupdatesystem();
sub check_update_system {
    my $method = '';
    if ( -e '/var/cpanel/useyum' ) {
        $method = 'yum';
        my $out = `yum info glibc 2>&1`;
        return if ( $out =~ m{ (?: Installed | Available) \s+ Packages }xmsi );
    }
    elsif ( -e '/var/cpanel/useup2date' ) {
        $method = 'up2date';
        my $out = `up2date --showall`;
        return if ( $out =~ /^glibc-/mi );
    }
    else {
        invalid_system('yum or up2date not detected.');
    }

    ERROR chr(27) . '[1;31m';
    ERROR q{Your operating system's rpm update method } . qq{($method) was not able to locate the glibc package. } . q{This is an indication of an improper setup. } . q{You must correct this error before you can proceed. };
    ERROR chr(27) . '[0;39m';
    FATAL "\n\n";
    exit 2;
}

sub cpfetch {
    my $url = shift or FATAL("cpfetch called without a URL?");
    my $target_file = shift;

    if ( $url !~ /^http/ ) {
        $url = 'http://' . get_update_source() . '/' . $url;
    }

    my $file;
    if ( !$target_file ) {
        my @FILE = split( /\//, $url );
        $file = pop(@FILE);
    }
    else {
        $file = $target_file;
    }

    if ( -e $file ) {
        WARN("Warning: Overwriting $file");
        unlink $file;
        FATAL("Cannot remove $file") if ( -e $file );
    }

    DEBUG "Retrieving $url to $file";
    `$wget_bin $wget_args '$file' $url`;

    if ( !-e $file || -z $file ) {
        unlink $file;
        FATAL "Unable to fetch file $file.";
    }
}

sub get_update_source {
    my $update_source = 'httpupdate.cpanel.net';
    my $source_file   = '/etc/cpsources.conf';
    if ( -r $source_file && -s $source_file ) {    # pull in from cpsources.conf if it's set.
        open( my $fh, "<", $source_file );
        while (<$fh>) {
            next if ( $_ !~ m/^\s*HTTPUPDATE\s*=\s*(\S+)/ );
            $update_source = "$1";
            FATAL("HTTPUPDATE is set to '$update_source' in $source_file") if ( !$update_source );
            last;
        }
    }

    return $update_source;
}

sub _MSG {
    my $level = shift;
    my $msg = shift || '';
    chomp $msg;

    my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime;
    my ( $package, $filename, $line ) = caller($message_caller_depth);
    my $stamp_msg = sprintf( "%04d-%02d-%02d %02d:%02d:%02d %4s (%5s): %s\n", $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $line, $level, $msg );

    print LOG $stamp_msg;
    print $stamp_msg;
}

sub os_service_stop {
    my $service = shift or die;

    if ( $distro =~ m/bsd/i ) {
        if ( $service =~ m/syslog/i ) {
            ssystem( '/etc/rc.d/syslogd', 'stop' );
        }
        else {
            DEBUG "Skipping stop of service '$service' on bsd";
        }
        return;
    }

    if ( !-e "/etc/init.d/$service" ) {
        DEBUG("Skipping shutdown of non-existent service: $service");
        return;
    }

    my $service_bin = '/sbin/service';
    DEBUG "Ensuring service $service is not running";
    ssystem( $service_bin, $service, 'stop' );
    ssystem( 'killall', $service );
    sleep 1;
    ssystem( 'killall', '-9', $service );
}

sub os_service_start {
    my $service = shift or die;

    if ( $distro =~ m/bsd/i ) {
        if ( $service =~ m/syslog/i ) {
            ssystem( '/etc/rc.d/syslogd', 'start' );
        }
        else {
            DEBUG "Skipping start of service '$service' on bsd";
        }
        return;
    }

    my $service_bin = '/sbin/service';
    DEBUG "  Starting service $service";
    ssystem( $service_bin, $service, 'start' );
}

sub os_service_restart {
    my $service = shift or die;

    if ( $distro =~ m/bsd/i ) {
        if ( $service =~ m/syslog/i ) {
            ssystem( '/etc/rc.d/syslogd', 'restart' );
        }
        else {
            DEBUG "Skipping restart of service '$service' on bsd";
        }
        return;
    }

    my $service_bin = '/sbin/service';
    DEBUG "Restarting service $service";
    ssystem( $service_bin, $service, 'restart' );
}

# Code previously located in the bootstrap script.
sub bootstrap {
    my ( $distro, $distro_version ) = @_;

    # Confirm perl version.
    if ( $] < 5.008 ) {
        print "Sorry, Perl 5.8.0 or better is required to run this installer.\n";
        exit 5;
    }

    validate_rhn_registration( $distro, $distro_version );

    # Assure wget/bzip2/gpg are installed for centhat. These packages are needed prior to sysup
    install_critical_packages( $distro, $distro_version );

    # Sync the clock.
    if ( !update_system_clock() ) {
        WARN( "Current system time is set to: " . `date` );
        WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        WARN("Unable to verify system time. The utility to set time from a remote host, rdate, is not installed yet.");
        WARN("If your system time is skewed greater than a few hours, then source compilations will subtly fail.");
        WARN("This may result in an overall installation failure.");
        WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    }

    setup_empty_directories($distro);

    # Setup yum/up2date touch files.
    setup_update_config( $distro, $distro_version );

    # BSD Sanity
    bsd_sanity_checks($distro);
}

sub setup_empty_directories {
    my $distro = shift or die;

    # mkdir some directories.
    INFO 'Setting up needed empty cpanel directories.';

    foreach my $dir (qw{/usr/local/cpanel /usr/local/cpanel/logs /var/cpanel /var/cpanel/tmp /var/cpanel/version}) {
        unlink $dir if ( -f $dir || -l $dir );

        if ( !-d $dir ) {
            DEBUG "mkdir $dir";
            mkdir( $dir, 0755 );
        }
    }
}

sub setup_update_config {
    my ( $distro, $distro_version ) = @_;

    # No touchfiles for BSD.
    return if ( $distro =~ m/bsd/i );

    # IF RHEL 4
    INFO("Setting up update flag files");
    if ( $distro eq 'redhat' && $distro_version < 5 ) {
        touch('/var/cpanel/useup2date');
    }
    else {
        touch('/var/cpanel/useyum');
        unlink('/var/cpanel/useup2date');
        touch('/var/cpanel/yum_rhn') if ( $distro eq 'redhat' );
    }

    INFO("Making sure GPG is setup before importing keys");
    system(qw{gpg --list-keys});

    INFO("Importing gpg keys for yum");
    if ( -e '/usr/share/rhn/RPM-GPG-KEY' ) {
        system( 'gpg', '--import', '/usr/share/rhn/RPM-GPG-KEY' );
        system( 'rpm', '--import', '/usr/share/rhn/RPM-GPG-KEY' );
    }

    # import CentOS 4 keys if that's what we are.
    if ( $distro eq 'centos' && $distro_version == 4 && open( my $fh, '<', '/etc/redhat-release' ) ) {
        my $ver = <$fh>;
        if ( $ver =~ m/4\.([012])/ ) {
            system( 'rpm', '--import', "http://mirror.centos.org/centos/4.$1/os/i386/RPM-GPG-KEY" );
        }
    }

    if ( -e '/usr/share/doc/centos-release-4/RPM-GPG-KEY-centos4' ) {
        system( 'rpm', '--import', '/usr/share/doc/centos-release-4/RPM-GPG-KEY-centos4' );
    }

    if ( !-e '/etc/yum.conf' && -e '/etc/centos-yum.conf' ) {
        INFO("Setting up yum from '/etc/centos-yum.conf'");
        system(qw{cp -f /etc/centos-yum.conf /etc/yum.conf});
    }
}

sub validate_rhn_registration {
    my ( $distro, $distro_version ) = @_;

    # Short here if not redhat
    return if ( $distro ne 'redhat' );

    INFO("Checking RedHat registration for updates");
    local $ENV{'TERM'} = 'dumb';
    my $registered = '';
    if ( $distro_version > 4 ) {
        $registered = `yum list < blank 2>&1`;
    }
    elsif ( $distro_version eq 4 ) {
        $registered = `up2date --show-channels --nox < blank 2>&1`;
    }
    else {
        FATAL("Unknown update system. Cannot register.");
    }

    if ( $registered =~ m/not register|Please run rhn_register/ms ) {
        ERROR("You must register with the Red Hat Network");
        ERROR("when using Red Hat Enterprise before you can install cPanel.");
        ERROR("Please run the following to register your server: /usr/sbin/rhn_register ");
        FATAL("Installer Terminating....");
    }

    return if ( $distro_version < 6 );

    my @channels = `/usr/sbin/spacewalk-channel --list`;
    INFO("Validating you are subscribed to the optional RHN channel");

    # optional channel validated.
    return if ( grep { m/-optional-\d/ } @channels );

    my $optional_channel;
    foreach my $channel (@channels) {
        chomp $channel;
        next if ( $channel !~ /^rhel-([\dxi_]+)-server-\d+$/i );
        $optional_channel = $channel;
        $optional_channel =~ s/-server-6/-server-optional-6/;
    }
    if ( !$optional_channel ) {
        ERROR("You do not appear to be registered with a known base channel for redhat");
        ERROR('$> /usr/sbin/spacewalk-channel --list');
        ERROR(`/usr/sbin/spacewalk-channel --list`);
        exit 8;
    }

    ERROR("cPanel requires you be subscribed to the RHEL 6 optional channel to get all of the needed packages.");
    ERROR("cPanel will not function without this channel. Please correct the situation and re-run the installer.");
    ERROR('$> /usr/sbin/spacewalk-channel --list');
    ERROR(`/usr/sbin/spacewalk-channel --list`);
    ERROR(" ");
    ERROR("Please run this command: /usr/sbin/spacewalk-channel --add --channel=$optional_channel");
    ERROR(" ");
    ERROR("Or you can register to the optional channel at http://rhn.redhat.com");
    FATAL("Terminating...");
}

sub upgrade_to_cloud_linux {
    DEBUG "Detecting if Cloud Linux is licensed through cpanel";

    # /ULC/cpanel can't run until nativedf is run.
    ssystem('/usr/local/cpanel/install/nativedf');

    my $license_options = `/usr/local/cpanel/cpanel -F`;
    return @_ if ( $license_options !~ m/cloudlinux/ms );

    my $cloud_installer = '/usr/local/cpanel/bin/cloudlinux_update';
    INFO("Upgrading your distro to Cloud Linux");
    if ( !-x $cloud_installer ) {
        WARN("Cannot convert your system to Cloud Linux without $cloud_installer");
    }
    else {
        ssystem($cloud_installer);
    }

    # Re-check system to make sure we haven't moved to cloud linux
    return check_system_support();
}

sub notify_if_boot_kernel_changed {
    my $distro = shift || '';

    return if ( $^O =~ m/bsd/i );    # Not a BSD check.

    open( my $fh, '<', '/boot/grub/grub.conf' ) or return;
    my $default;
    while ( my $line = <$fh> ) {
        next if ( $line !~ m/^\s*default\s*=\s*(\d+)/ );
        $default = "$1";
        last;
    }
    if ( !defined $default ) {
        WARN("Cannot determine default boot from /boot/grub/grub.conf. You may need to reboot");
        return;
    }

    # Walk the file to find the $default boot's title
    my $title;
    for ( 0 .. $default ) {
        while ( my $line = <$fh> ) {
            if ( $line =~ m/^title\s/ ) {
                $title = $line;
                chomp $title;
                last;
            }
        }
    }

    # Look for the kernel.
    my $boot_kernel;
    while ( my $line = <$fh> ) {
        return WARN("Cannot determine default boot from /boot/grub/grub.conf. for boot entry '$title' You may need to reboot -- $line")
          if ( $line =~ m/^title\s/ );
        next if ( $line !~ m{^\s*kernel\s+\S*/vmlinuz-(\S+)\s}i );
        $boot_kernel = "$1";
        last;

    }
    return WARN("Cannot determine default boot from /boot/grub/grub.conf. You may need to reboot")
      if ( !$boot_kernel );

    my $current_kernel = `uname -r`;
    chomp $current_kernel;
    return if ( $current_kernel eq $boot_kernel );

    WARN "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
    WARN "Your system kernel may have been updated.";
    WARN "Current kernel ($current_kernel) has been changed to: $boot_kernel";
    WARN "Before rebooting the system, please ensure that the installed kernel version is compatible with your deployment.";
    if ( $distro =~ m/cloud/i ) {
        WARN " ";
        WARN " ************************************************************************************************************";
        WARN " ";
        WARN "   NOTE: Because this is a Cloud Linux install, cPanel WILL NOT BE FULLY FUNCTIONAL until you reboot. ";
        WARN " ";
        WARN " ************************************************************************************************************";
        WARN " ";
    }
    WARN "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
}

sub install_critical_packages {
    my ( $distro, $distro_version ) = @_;

    return if ( $distro =~ m/bsd/i );    # We can't do bsd this early in the installer.

    # This is a hail mary to try to get these in and prevent updatenow/yum from failing in wierd ways.
    # If the install fails and these did too, it's probably the cause.
    foreach (qw/wget bzip2 gnupg rdate/) {
        my $critical_pkg = $_;           # Permit modification on this variable.
        $critical_pkg .= '2' if ( $critical_pkg eq 'gnupg' && $distro_version >= 6 );    # C6 decided to rename their gnupg package to gnupg2
        `rpm -q $critical_pkg`;
        next if ( !$? );

        WARN "Attempting to install critical package '$critical_pkg' for the installer";

        unless ( $distro eq 'redhat' && $distro_version == 4 ) {
            ssystem( qw/yum -y install/, $critical_pkg );
        }
        elsif ( $distro eq 'redhat' ) {
            ssystem( qw{/usr/sbin/up2date -i}, $critical_pkg );
        }
    }

}

sub get_download_tool_binary {
    my ( $distro, $distro_version ) = @_;

    for my $bin (qw(/bin/wget /usr/bin/wget /usr/local/bin/wget)) {
        next if ( !-e $bin );
        next if ( !-x _ );
        next if ( -z _ );
        return ( $bin, '-q --tries=3 --timeout=60 --dns-timeout=60 -O' ) if ( `$bin --version` =~ m/GNU\s+Wget\s+\d+\.\d+/ims );
    }

    if ( $distro =~ m/bsd/i ) {
        for my $bin (qw(/usr/bin/fetch /usr/local/bin/fetch /bin/fetch)) {
            next if ( !-e $bin );
            next if ( !-x _ );
            next if ( -z _ );
            return ( $bin, '-q -T 60 -o' );
        }
        FATAL "Neither wget nor fetch found, please install one to a standard location";
    }

    FATAL "The wget binary could not be found. Please install it to a standard location";
}

sub bsd_sanity_checks {
    my $distro = shift;
    return if ( $distro !~ m/bsd/i );

    if ( !-e '/proc/1' ) {
        INFO "Mounting procfs on /proc";
        ssystem( 'mount', '-t', 'procfs', '/proc', '/proc' );
    }

    if ( !-e '/usr/local/bin/rsync' && !-e '/usr/bin/rsync' ) {
        if ( -e '/usr/sbin/pkg_add' ) {
            INFO "Installing rsync";
            ssystem( '/usr/sbin/pkg_add', '-r', 'rsync' );
        }
        if ( !-e '/usr/local/bin/rsync' && !-e '/usr/bin/rsync' ) {
            FATAL "The rsync binary could not be installed";
        }
    }

    setup_bsd_symlinks();
}

sub setup_bsd_symlinks {
    my %symlinks = qw{
      /usr/bin/gzip /bin/gzip
      /usr/bin/false /bin/false
      /sbin/md5 /bin/md5
      /usr/bin/tar /bin/tar
      /etc/namedb /var/named
      /usr/sbin/rndc /usr/sbin/ndc
      /usr/local/bin/ncftpget /usr/bin/ncftpget
      /usr/local/bin/lynx /usr/bin/lynx
      /usr/local/bin/bash /bin/bash
      /usr/local/bin/expect /usr/bin/expect
      /usr/local/sbin/exim /usr/sbin/exim
      /var/db/mysql /var/lib/mysql
      /usr/home /home
      /usr/local/bin/gmake /usr/bin/gmake
    };

    mkdir('/var/lib');     # For /var/lib/mysql
    mkdir('/usr/home');    # default home directory isn't necessarily setup on bsd systems.
    touch('/etc/make.conf') if ( !-e '/etc/make.conf' );

    # Setup a forced symlink for each of these.
    while ( my ( $bsd_location, $linux_location ) = each %symlinks ) {
        next if ( -e $linux_location );
        ssystem( qw{ln -sf}, $bsd_location, $linux_location );
    }
}

sub install_bsd_software {
    touch('/var/cpanel/usebsdpkgs');

    ssystem('/usr/local/cpanel/scripts/setupmakeconf');
    ssystem('/usr/local/cpanel/scripts/fixbinpath');
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'rsync' );

    ssystem('/usr/local/cpanel/scripts/portsup');

    # Case #51804
    INFO "Building openssl in with STDOUT not redirected to work around bug in openssl";
    system( '/usr/local/cpanel/scripts/ensurepkg', 'openssl' );

    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'expect-devel' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'libltdl' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'expat' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'gd' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'p5-XML-Parser' );    # handles installing perl modules better
    if ( -e '/usr/local/bin/use.perl' ) {
        ssystem( '/usr/local/bin/use.perl', 'port' );
        if ( !-e '/usr/bin/perl5' and !-l '/usr/bin/perl5' ) {
            ssystem( 'ln', '-sf', '/usr/local/bin/perl', '/usr/bin/perl5' );
        }
    }
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat3x' );
    if ( $distro_version < 7.99999 ) {
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat4x' );
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat5x' );
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat6x' );
    }
    elsif ( $distro_version > 7.9999 ) {
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat4x' );
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat5x' );
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat6x' );
        ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'compat7x' );
    }
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'rdate' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'bind9' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'python' );

    # needed due to bad pth on freebsd default install which breaks gnupg
    my $installer_dir = Cwd::getcwd() || `pwd`;
    schdir('/usr/ports/devel/pth');
    ssystem('make deinstall');
    ssystem('make clean');
    ssystem('make install clean');
    schdir($installer_dir);
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'gnupg' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'wget' );
    ssystem( '/usr/local/cpanel/scripts/ensurepkg', 'libiconv' );
    ssystem('/usr/local/cpanel/scripts/installfpfreebsd');
    ssystem('/usr/local/cpanel/scripts/sysup');
}

sub enable_cphulkd {
    my $conf_dir     = '/var/cpanel/hulkd';
    my $enabled_file = "$conf_dir/enabled";

    INFO "Enabling cphulkd ...";
    mkdir '/var/cpanel', 0755 unless -e '/var/cpanel';
    mkdir "$conf_dir",   0755 unless -e "$conf_dir";

    if ( open my $enabled_fh, '>', $enabled_file ) {
        print {$enabled_fh} time();
        close $enabled_fh;
        INFO "Done";
    }
    else {
        WARN "Unable to enable cphulkd";
    }
}

# This code is somewhat of a duplication of the code for updatenow that blocks updates based on configuration
# settings. It needs to be here also because of the bootstrap level nature for when this needs to run.
sub check_for_install_version_blockers {
    my ( $distro, $distro_version ) = @_;

    # pull in cpanel.config settings.
    my $cpanel_config = read_config('/var/cpanel/cpanel.config');

    # Pull in cpupdate.conf settings.
    my $cpupdate_conf = read_config('/etc/cpupdate.conf');

    # Determine tier or assume defaults.
    my $tier = $cpupdate_conf->{'CPANEL'} || ( -e '/var/cpanel/dnsonly' ? 'stable' : 'release' );
    $tier =~ s/^(\d+\.\d+)[\d.]+?$/$1/;    # Strip special versioned tiers down to 2 numbered.

    if ( $distro =~ m/bsd/i && $tier ne '11.30' ) {
        WARN('Setting installation tier to "11.30" since this is the only version supported for FreeBSD (see http://www.cpanel.net/products/cpanelwhm/system-requirements.html)');
        $tier = $cpupdate_conf->{'CPANEL'} = '11.30';
        save_config( '/etc/cpupdate.conf', $cpupdate_conf );
    }

    if ( $cpanel_config->{'mysql-version'} =~ m/5\.5/ && $tier eq '11.30' ) {
        FATAL "Sorry, you have set MySQL to 5.5 in '/var/cpanel/cpanel.config' and your tier to 11.30 in '/etc/cpupdate.conf'. These are incompatible custom settings.";
    }

    if ( !$cpanel_config->{'maildir'} && $tier ne '11.30' ) {
        FATAL "Sorry, you are trying to install exim with mbox '/var/cpanel/cpanel.config' on a cPanel version after 11.30. These are incompatible custom settings.";
    }

}

sub update_system_clock {
    my @rdate_bin =
        -x '/scripts/rdate' ? ('/scripts/rdate')
      : -x '/usr/bin/rdate'        ? ( '/usr/bin/rdate',        '-s', 'rdate.cpanel.net' )
      : -x '/usr/local/bin/rdate'  ? ( '/usr/local/bin/rdate',  '-s', 'rdate.cpanel.net' )
      : -x '/usr/local/sbin/rdate' ? ( '/usr/local/sbin/rdate', '-s', 'rdate.cpanel.net' )
      : -x '/bin/rdate'            ? ( '/bin/rdate',            '-s', 'rdate.cpanel.net' )
      :                              ();

    # Complain if we don't have an rdate binary.
    if ( !@rdate_bin ) {
        ERROR("Could not set system clock. Missing an rdate binary");
        return;
    }

    # Set the clock
    my $was = time();
    ssystem(@rdate_bin);
    my $now = time();
    INFO( "Clock set to: " . localtime($now) );
    my $change = $now - $was;

    # Adjust the start time if it shifted more than 10 seconds.
    if ( abs($change) > 10 ) {
        WARN("Clock changed by $change seconds.");
        $installstart += $change;
        WARN( "Starting time adjusted to " . localtime($installstart) );
    }
    else {
        INFO("Clock changed by $change seconds");
    }

    return 1;
}