package Cpanel::LVEInfo;

BEGIN {
    unshift @INC, '/usr/local/cpanel', '/usr/local/cpanel/whostmgr/docroot/3rdparty/cloudlinux',
        '/usr/share/l.v.e-manager/cpanel/cgi';
    #use CGI::Carp qw(fatalsToBrowser); # for detail comments
}
# cpanel - Cpanel/LVEInfo.pm                  Copyright(c) 2011 CloudLinux, Inc.
#                                                           All Rights Reserved.
# info@cloudlinux.com                                  http://www.cloudlinux.com
# This module is free software; you can redistribute it and/or modify it under
# the same terms as Perl itself. See http://dev.perl.org/licenses/artistic.html
# CloudLinux LVE Manager VERSION:0.83

use experimental 'smartmatch';
use JSON::XS;
use Cpanel::Math     ();
use Cpanel::Locale   ();
use Cpanel::JSON     ();
use version;
use CloudLinux;
# safe import Cpanel::StatsBar fo support check syntax by /usr/local/cpanel/3rdparty/bin/perl -cw LVEInfo.pm
if ( eval{ require Cpanel::StatsBar; 1; } ) { Cpanel::StatsBar->import() };

my $mark_phrase_module;

# On some early cPanel versions, 'Locale::Maketext::Utils::MarkPhrase' does not exist
# and 'Cpanel::CPAN::Locale::Maketext::Utils::MarkPhrase' is used instead
BEGIN {
    eval {
        require Locale::Maketext::Utils::MarkPhrase;
        Locale::Maketext::Utils::MarkPhrase->import();
        $mark_phrase_module = 'Locale::Maketext::Utils::MarkPhrase';
        1;
    } or do {
        require Cpanel::CPAN::Locale::Maketext::Utils::MarkPhrase;
        Cpanel::CPAN::Locale::Maketext::Utils::MarkPhrase->import();
        $mark_phrase_module = 'Cpanel::CPAN::Locale::Maketext::Utils::MarkPhrase';
    };
}

my @lve;
my @def;
my @quota;
my $locale;
my $inside_openvz = CloudLinux::safeRun('/usr/bin/cldetect', '--check-openvz');
if ($inside_openvz + 0 ne $inside_openvz) {$inside_openvz = 0}; # $inside_openvz must be a number

sub get_cpanel_version{
    my $filename = '/usr/local/cpanel/version';
    my $version  = Cpanel::LoadFile::loadfile($filename);
    chomp $version;
    return $version;
}

my $cpanel_version = get_cpanel_version();

sub max {
    my $x = shift;
    my $y = shift;
    return ( $x > $y ? $x : $y );
}

sub version_compare {
    my $ver1 = shift || 0;
    my $ver2 = shift || 0;
    my @v1 = split /[.+:~-]/, $ver1;
    my @v2 = split /[.+:~-]/, $ver2;
    for ( my $i = 0 ; $i < max( scalar(@v1), scalar(@v2) ) ; $i++ ) {
        # Add missing version parts if one string is shorter than the other
        # i.e. 0 should be lt 0.2.1 and not equal, so we append .0
        # -> 0.0.0 <=> 0.2.1 -> -1
        push( @v1, 0 ) unless defined( $v1[$i] );
        push( @v2, 0 ) unless defined( $v2[$i] );
        if ( int( $v1[$i] ) > int( $v2[$i] ) ) {
            return 1;
        }
        elsif ( int( $v1[$i] ) < int( $v2[$i] ) ) {
            return -1;
        }
    }
    return 0;
}


sub get_lve_info {
    my ($id) = @_;
    @quota = @{ get_quota_info($id) };
    if ( open my $fh, '<', '/var/lve/info' ) {
        while ( my $line = readline $fh ) {
            chomp $line;
            my @vals = split( /,/, $line );
            if ( $vals[0] == $id ) {
                close $fh;
                return @vals;
            }
            elsif ( $vals[0] eq '0' ) {
                @def = @vals;
            }
        }
        close $fh;
    }
    return;
}

sub get_quota_info {
    my ($id) = @_;
    my @params = ('/usr/bin/cl-quota', "--user-id=$id", '--csv');
    my $rv = CloudLinux::safeRun(@params);
    my ($header, $data) = split(/\n/, $rv, 2);
    chomp $data;
    return [split(/,/, $data)];
}

sub api2_getSummary {
    my %OPTS = @_;
    $locale ||= Cpanel::Locale->get_handle();
    my $inodes = $OPTS{'lveinodes'};
    my @fields = qw/id mep lep cpu_usage lcpu mem_usage lmem mem_fault
                    mep_fault lmemphy memphy memphy_fault lnproc nproc
                    nproc_fault lcpuw iousage iolimit liops iops cpuf iopsf iof/;

    my %data = (
       cpu   => { t => 'CPU Usage', u => 'cpu_usage', l => 'lcpu', f => 'cpuf' },
       mep   => { t => 'Entry Processes', u => 'mep', l => 'lep', f => 'mep_fault' },
       vmem  => { t => 'Virtual Memory Usage', u => 'mem_usage', l => 'lmem', f => 'mem_fault' },
       pmem  => { t => 'Physical Memory Usage', u => 'memphy', l => 'lmemphy', f => 'memphy_fault' },
       nproc => { t => 'Number of Processes', u => 'nproc', l => 'lnproc', f => 'nproc_fault' },
       io    => { t => 'I/O Usage', u => 'iousage', l => 'iolimit', f => 'iof' },
       iops  => { t => 'IOPS', u => 'iops', l => 'liops', f => 'iopsf'},
    );
    if ($inside_openvz) {
        %data = filter_dict(\%data, 'mep')
    };

    my $normalized_user_cpu = is_normalize_user_cpu();

    my $doRoundK = sub {
        my $v = shift;
        if ( $v < 1024 ) {
            return sprintf "%0.2fk", $v;
        }
        elsif ( $v >= 1024 and $v < 1048576 ) {
            return sprintf "%0.2fM", $v / 1024;
        }
        else {
            return sprintf "%0.2fG", $v / 1048576;
        }
    };
    if ( open my $fh, '<', '/var/lve/info' ) {
        my %vals = ();
        while ( my $line = readline $fh ) {
            chomp $line;
            my @v = split( /,/, $line );
            my $key = $v[0] == 0 ? 'default' : $v[0];
            for my $i ( 1 .. $#v ) {
                $vals{$key}->{ $fields[$i] } = $v[$i];
            }
        }
        close $fh;
        my $user = getuid();
        if ($inodes) {
            $data{inodes} = { t => 'inodes usage', u => 'inodesusage', l => 'inodeslimit' };
            my @quota = @{ get_quota_info($user) };
            for my $key ( keys %vals ) {
                $vals{$key}->{inodesusage} = $quota[1];
                $vals{$key}->{inodeslimit} = $quota[2];
            }
        }
        my $arref = [];
        my $keyname = ( defined $user and exists $vals{$user} ) ? $user : 'default';

        for my $key ( sort keys %data ) {
            #next if $vals{$keyname}->{ $data{$key}->{l} } == 0;
            my $usage = 0;
            my $limit = 0;
            if ($key eq 'cpu') {
                if ($normalized_user_cpu) {
                    $usage = sprintf("%.1f", ( $vals{$keyname}->{ $data{$key}->{u} } * 100 / ( $vals{$keyname}->{ $data{$key}->{l} } || 1 ) ) ).'%';
                    $limit = '100%';
                } else {
                    $usage = sprintf("%.1f", ( $vals{$keyname}->{ $data{$key}->{u} } / 100 ) ).'%';
                    $limit = ($vals{$keyname}->{ $data{$key}->{l} } / 100).'%';
                }
            } elsif ($key eq 'io') {
                $usage = sprintf("%.1f", ( $vals{$keyname}->{ $data{$key}->{u} } ) ).' KB/s';
                $limit = sprintf("%.1f", ( $vals{$keyname}->{ $data{$key}->{l} } ) ).' KB/s';
            } else {
                $usage = (($key eq 'pmem' or $key eq 'vmem')
                                ? $doRoundK->( $vals{$keyname}->{ $data{$key}->{u} } * 4 )
                                : $vals{$keyname}->{ $data{$key}->{u} } );
                $limit = (($key eq 'pmem' or $key eq 'vmem') ? $doRoundK->( $vals{$keyname}->{ $data{$key}->{l} } * 4 ) : $vals{$keyname}->{ $data{$key}->{l} });
            }
            push @{ $arref },
                {
                    title=>$locale->maketext($data{$key}->{t}),
                    usage=>$usage,
                    limit=>$limit,
                    faults=>(exists $data{$key}->{f} ? $vals{$keyname}->{ $data{$key}->{f} } : '-'),
                } unless $vals{$keyname}->{ $data{$key}->{l} } == 0;
        }
        return $arref;
    }
    return;
}

sub LVEInfo_init {

    my $uid = getuid();
    @lve = get_lve_info($uid);

    Cpanel::StatsBar::_load_stats_ref();

    if (has_new_config_value("hideLVEUserStat", "uiSettings")) {
        return;
    }

    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'module'}  = 'LVEInfo';
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'_count'}  = \&Cpanel::LVEInfo::_cpu;
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'_max'}    = \&Cpanel::LVEInfo::_cpu_limit;
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'langkey'} = 'CPU Usage';
    # $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'_max'}    = '100 %';
    # $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'units'}   = '%';
    # for cPanel >= 11.46
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'phrase'} = $mark_phrase_module->can('translatable')->('CPU Usage');
    # for cPanel 11.54+
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'pc'} = percent(_cpu(), _cpu_limit());
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'phrase'};
    $Cpanel::StatsBar::rSTATS->{'lvecpu'}{'file'} = "cpu";

    if (!_is_mem_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'_count'}  = \&Cpanel::LVEInfo::_mem;
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'_max'}    = \&Cpanel::LVEInfo::_mem_limit;
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'langkey'} = 'Virtual Memory Usage';
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'module'}  = 'LVEInfo';
        if (version_compare($cpanel_version, '11.57') == 1){
            $Cpanel::StatsBar::rSTATS->{'lvemem'}{'units'}   = 'MB';
        }
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'phrase'} = $mark_phrase_module->can('translatable')->('Virtual Memory Usage');
        # for cPanel 11.54+
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'pc'} = percent(_mem(), _mem_limit());
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lvemem'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lvemem'}{'file'} = "mem";
    }

    if (!Cpanel::LVEInfo::_is_pmem_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'module'} = 'LVEInfo';
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'_count'}  = \&Cpanel::LVEInfo::_pmem;
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'_max'}    = \&Cpanel::LVEInfo::_pmem_limit;
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'langkey'} = 'Physical Memory Usage';
        if (version_compare($cpanel_version, '11.57') == 1){
            $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'units'}   = 'MB';
        }
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'phrase'} = $mark_phrase_module->can('translatable')->('Physical Memory Usage');
        # for cPanel 11.54+
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'pc'} = percent(_pmem(), _pmem_limit());
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lvepmem'}{'file'} = "pmem";
    }

    if (!Cpanel::LVEInfo::_is_mep_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'module'}  = 'LVEInfo';
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'_count'}  = \&Cpanel::LVEInfo::_mep;
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'_max'}    = \&Cpanel::LVEInfo::_mep_limit;
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'langkey'} = 'Entry Processes';
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'phrase'} = $mark_phrase_module->can('translatable')->('Entry Processes');
        # for cPanel 11.54+
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'pc'} = percent(_mep(), _mep_limit());
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lvemep'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lvemep'}{'file'} = "mep";
    }

    if (!Cpanel::LVEInfo::_is_iop_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'module'}  = 'LVEInfo';
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'_count'}  = \&Cpanel::LVEInfo::_iop;
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'_max'}    = \&Cpanel::LVEInfo::_iop_limit;
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'langkey'} = 'I/O Usage';
        # CPANEL use UNITS as a marker for MB and IF units exists - interpretate information as a memory in bytes (ver 11.65\11.66)
        # so it does not metter what unit here
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'units'}   = 'KB/s';
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'phrase'} = $mark_phrase_module->can('translatable')->('I/O Usage');
        # for cPanel 11.54+
        # Cpanel does not use this function in version (11.65\11.66)
        # for percent calculation Cpanel use _count and _max. If there are some strings - percent will be always 0
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'pc'} = percent(_iop(), _iop_limit());
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lveiop'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lveiop'}{'file'} = "iop";
    }

    if (!Cpanel::LVEInfo::_is_proc_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'module'}  = 'LVEInfo';
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'_count'}  = \&Cpanel::LVEInfo::_proc;
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'_max'}    = \&Cpanel::LVEInfo::_proc_limit;
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'langkey'} = 'Number of Processes';
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'phrase'} = $mark_phrase_module->can('translatable')->('Number of Processes');
        # for cPanel 11.54+
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'pc'} = percent(_proc(), _proc_limit());
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lveproc'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lveproc'}{'file'} = "proc";
    }

    if (!has_config_value("file_usage") && has_new_config_value("showUserInodesUsage", "inodeLimits") && !Cpanel::LVEInfo::_is_quota_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'module'}  = 'LVEInfo';
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'_count'}  = \&Cpanel::LVEInfo::_quota;
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'_max'}    = \&Cpanel::LVEInfo::_quota_limit;
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'langkey'} = 'inodes';
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'phrase'} = $mark_phrase_module->can('translatable')->('inodes');
        # for cPanel 11.54+
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'pc'} = percent(_quota(), _quota_limit());
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lvequota'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lvequota'}{'file'} = "quota";
    }

    if (!Cpanel::LVEInfo::_is_iops_unlimited()) {
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'module'}  = 'LVEInfo';
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'_count'}  = \&Cpanel::LVEInfo::_iops;
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'_max'}    = \&Cpanel::LVEInfo::_iops_limit;
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'langkey'} = 'IOPS';
        # for cPanel >= 11.46
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'phrase'} = $mark_phrase_module->can('translatable')->('IOPS');
        # for cPanel 11.54+
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'pc'} = percent(_iops(), _iops_limit());
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'itemdesc'} = $Cpanel::StatsBar::rSTATS->{'lveiops'}{'phrase'};
        $Cpanel::StatsBar::rSTATS->{'lveiops'}{'file'} = "iops";
    }
}

# Format of line in /var/lve/info (formed by lvestats-server):
# (id, mep, lep, cpu_usage, lcpu, mem_usage, lmem,
# mem_fault, mep_fault, lmemphy, memphy, memphy_fault,
# lnproc, nproc, nproc_fault, lcpuw, ioread, iowrite)
use constant ID_INDEX     => 0;
use constant MEP_INDEX    => 1;
use constant LEP_INDEX    => 2;
use constant CPU_INDEX    => 3;
use constant LCPU_INDEX   => 4;
use constant MEM_INDEX    => 5;
use constant LMEM_INDEX   => 6;
use constant MEMF_INDEX   => 7;
use constant MEPF_INDEX   => 8;
use constant LPMEM_INDEX  => 9;
use constant PMEM_INDEX   => 10;
use constant PMEMF_INDEX  => 11;
use constant LNPROC_INDEX => 12;
use constant NPROC_INDEX  => 13;
use constant NPROCF_INDEX => 14;
use constant LCPUW_INDEX  => 15;
use constant IOUSAGE_INDEX => 16;
use constant IOLIMIT_INDEX => 17;
use constant IOPSLIMIT_INDEX => 18;
use constant IOPSUSAGE_INDEX => 19;

sub convert_mem_for_cpanel_11_57{
    my ( $val ) = @_;
    my $rv = $val * 4 * 1024;
    return sprintf $rv;

}

sub _mem {
    if (version_compare($cpanel_version, '11.57') == 1){
        return convert_mem_for_cpanel_11_57($lve[MEM_INDEX])  if (@lve);
    }
    return k_to_m($lve[MEM_INDEX] << 2) if (@lve);
    return 0;
}

sub round_mem {
    return RoundMemK( $lve[MEM_INDEX] << 2 ) if (@lve);
    return 0;
}

sub LVEInfo_mem {
    print round_mem();
}

sub _mem_limit {
    if (version_compare($cpanel_version, '11.57') == 1){
        return convert_mem_for_cpanel_11_57($lve[LMEM_INDEX]) if (@lve);
        return convert_mem_for_cpanel_11_57($def[LMEM_INDEX]);
    }
    return k_to_m($lve[LMEM_INDEX] << 2).' MB' if (@lve);
    return k_to_m($def[LMEM_INDEX] << 2).' MB';
}

sub _is_mem_unlimited {
    if (@lve) {
        return ($lve[LMEM_INDEX] == 0);
    }
    if (@def) {
        return ($def[LMEM_INDEX] == 0);
    }
    return 1;
}

sub round_mem_limit {
    return RoundMemK( $lve[LMEM_INDEX] << 2 ) if (@lve);
    return RoundMemK( $def[LMEM_INDEX] << 2 );
}

sub LVEInfo_mem_limit {
    print round_mem_limit();
}

sub _cpu {
    if (is_normalize_user_cpu()) {
        return (@lve && $lve[LCPU_INDEX] != 0)
                ? int($lve[CPU_INDEX] / $lve[LCPU_INDEX] * 100)
                : 0;
    } else {
        return int($lve[CPU_INDEX]/100);
    }
}

sub LVEInfo_cpu {
    print _cpu();
}

sub _cpu_limit {
    if (is_normalize_user_cpu()) {
        if (version_compare($cpanel_version, '11.57') == 1){
            return '100';
        }
        return '100%';
    } else {
        if (version_compare($cpanel_version, '11.57') == 1){
            return ($lve[LCPU_INDEX] / 100);
        }
        return ($lve[LCPU_INDEX] / 100).'%';
    }
}

sub LVEInfo_cpu_limit {
    print _cpu_limit();
}

sub getuid() {
    my $user = $Cpanel::user;
    return ( Cpanel::PwCache::getpwnam($user) )[2];
}

sub LVEInfo_start {
}

sub _mep {
    if   (@lve) { return $lve[MEP_INDEX]; }
    else        { return 0; }
}

sub LVEInfo_mep {
    print _mep();
}

sub _mep_limit {
    if   (@lve) { return $lve[LEP_INDEX]; }
    else        { return $def[LEP_INDEX]; }
}

sub _is_mep_unlimited {
    if (@lve) {
        return ($lve[LEP_INDEX] == 0);
    }
    if (@def) {
        return ($def[LEP_INDEX] == 0);
    }
    return 1;
}

sub LVEInfo_mep_limit {
    print _mep_limit();
}

sub _pmem {
    if (version_compare($cpanel_version, '11.57') == 1){
        return convert_mem_for_cpanel_11_57($lve[PMEM_INDEX]) if (@lve);
    }
    return k_to_m($lve[PMEM_INDEX] << 2) if (@lve);
    return 0;
}

sub LVEInfo_pmem {
    print _pmem();
}

sub _pmem_limit {
    if (version_compare($cpanel_version, '11.57') == 1){
        return convert_mem_for_cpanel_11_57($lve[LPMEM_INDEX])  if (@lve);
        return convert_mem_for_cpanel_11_57($def[LPMEM_INDEX]);
    }
    return k_to_m($lve[LPMEM_INDEX] << 2).' MB' if (@lve);
    return k_to_m($def[LPMEM_INDEX] << 2).' MB';
}

sub LVEInfo_pmem_limit {
    print _pmem_limit();
}

sub _is_pmem_unlimited {
    if (@lve) {
        return ($lve[LPMEM_INDEX] == 0);
    }
    if (@def) {
        return ($def[LPMEM_INDEX] == 0);
    }
    return 1;
}

sub _proc {
    if   (@lve) { return $lve[NPROC_INDEX]; }
    else        { return 0; }
}

sub LVEInfo_proc {
    print _proc();
}

sub _proc_limit {
    if   (@lve) { return $lve[LNPROC_INDEX]; }
    else        { return $def[LNPROC_INDEX]; }
}

sub LVEInfo_proc_limit {
    print _proc_limit();
}

sub _is_proc_unlimited {
    if (@lve) {
        return ($lve[LNPROC_INDEX] == 0);
    }
    if (@def) {
        return ($def[LNPROC_INDEX] == 0);
    }
    return 1;
}


sub _iops {
    if   (@lve) { return $lve[IOPSUSAGE_INDEX]; }
    else        { return 0; }
}

sub LVEInfo_iops {
    print _iops();
}

sub _iops_limit {
    if   (@lve) { return $lve[IOPSLIMIT_INDEX]; }
    else        { return $def[IOPSLIMIT_INDEX]; }
}


sub LVEInfo_iops_limit {
    print _iops_limit();
}

sub _is_iops_unlimited {
    if (@lve) {
        return ($lve[IOPSLIMIT_INDEX] == 0);
    }
    if (@def) {
        return ($def[IOPSLIMIT_INDEX] == 0);
    }
    return 1;
}

sub _iop {
    if   (@lve) { return $lve[IOUSAGE_INDEX] << 10; }
    else        { return 0; }
}

sub LVEInfo_iop {
    print _iop();
}

sub _iop_limit {
    if   (@lve) { return $lve[IOLIMIT_INDEX] << 10; }
    else        { return $def[IOLIMIT_INDEX] << 10; }
}

sub _is_iop_unlimited {
    if (@lve) {
        return ($lve[IOLIMIT_INDEX] == 0);
    }
    if (@def) {
        return ($def[IOLIMIT_INDEX] == 0);
    }
    return 1;
}

sub LVEInfo_iop_limit {
    print _iop_limit();
}

sub _quota {
    if   (@quota) { return $quota[1]; }
    else        { return 0; }
}

sub LVEInfo_quota {
    print _quota();
}

sub _quota_limit {
    if   (@quota) { return $quota[2]; }
    else        { return 0; }
}

sub _is_quota_unlimited {
    return 1 unless @quota;
    return 1 if $quota[2] == 0;
    return 0;
}

sub LVEInfo_quota_limit {
    print _quota_limit();
}

sub isError {
    my $x = shift;
    return $x ? 'errors' : 'lveok';
}

sub api2_hideIfNative {
    my $user = scalar getpwuid($<);
    my $param_string = qq#/usr/bin/selectorctl --user-current --interpreter=php --user=$user|#;
    open $p, $param_string;
    my $raw_string = do { local $/; <$p> };
    close $p;
    return [{state => $raw_string =~ /native/ ? q#style=display:none;# : q##}];
}

sub api2_getUsage2 {
    my %CFG = @_;
    my @RSD;
    my %attrs = (
        lEP     => [ qw/aEP mEP lEP/ ],
        lVMem    => [ qw/aVMem mVMem lVMem/ ],
        lPMem => [ qw/aPMem mPMem lPMem/ ],
        lNproc  => [ qw/aNproc mNproc lNproc/ ],
        lIO  => [ qw/aIO mIO lIO/ ],
        lIOPS =>[ qw/aIOPS mIOPS lIOPS/ ],
    );

    my $show_headers_filter = '.*';
    if ($inside_openvz) {
        $show_headers_filter = 'From|To|aEP|mEP|lEP|EPf';
    }
    push @RSD, lvestats_ver();

    my $lveid   = getuid();
    my $period  = !exists $CFG{'period'} || $CFG{'period'} eq '' ? '1d' : $CFG{'period'};
    my $timeUnit  = !exists $CFG{'timeUnit'} || $CFG{'timeUnit'} eq '' ? '1h' : $CFG{'timeUnit'};
    my @command = ( '/usr/sbin/lveinfo', '--csv', '--compat=v2', '--style=user', '--show-all',  "--id=$lveid", "--time-unit=$timeUnit");

    if(grep {$period eq $_} qw(7d 30d)){
        $endTimestamp = time();
        $startTimestamp = $endTimestamp - 86400 * $period;
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($startTimestamp);
        $year += 1900; $mon += 1;
        $startDate = "$year-$mon-$mday 00:00";
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($endTimestamp);
        $year += 1900; $mon += 1;
        $endDate = "$year-$mon-$mday 23:59";
        push @command, ( '--from', "$startDate", '--to', "$endDate" );
    } else {
        push @command, ( "--period=$period" );
    }
    my $INPUT   = CloudLinux::safeRun( @command );
    my @results = split( /\n+/, $INPUT );
    my @header = split( /,/, shift @results);
    $header[$#header] =~ s/^\s+//;
    $header[$#header] =~ s/\s+$//;
    return if !@results;  # return undef if there has been no activity
    my @show_column_index =  grep { $header[$_] =~ /$show_headers_filter/ } 0..$#header;

    my @header_filtered = map {$header[$_]} @show_column_index;
    my %limits = ( lEP => 0, lVMem => 0, lPMem => 0, lNproc => 0, lIO => 0, lIOPS => 0 );
    for my $i (@results) {
        my @data = split( /,/, $i );
        $data[0] =~ s/\s/&nbsp;/;
        $data[1] =~ s/\s/&nbsp;/;
        my %struct = map { $header[$_] => (
                               $header[$_] =~ /(?:a|m)(?:v|p)mem/i
                                   ? sprintf("%0.2f",$data[$_]/1048576)
                                   : $header[$_] =~ /l(?:v|p)mem/i
                                       ? sprintf("%d",$data[$_]/1048576)
                                       : $data[$_]
                                          ) } @show_column_index;
        for my $k ( keys %limits ) {
            $limits{$k} += $struct{$k};
        }
        push @RSD, \%struct;
    }
    my @limits_keys = keys %limits;
    for my $i ( @RSD ) {
        for my $k ( @limits_keys ) {
            if ( $limits{$k} == 0 ) {
                for my $j ( @{ $attrs{$k} } ) {
                    delete $i->{$j};
                }
            }
        }
    }
    return \@RSD;
}

sub api2_getUsage {
    my %CFG = @_;
    my @RSD;
    my $show_columns_v1 = "From To aCPU mCPU lCPU aEP mEP lEP aVMem mVMem lVMem VMemF EPf aPMem mPMem lPMem aNproc mNproc lNproc PMemF NprocF aIO mIO lIO aIOPS mIOPS lIOPS";
    my $show_columns_v2 = "From,To,aCPU,mCPU,lCPU,aEP,mEP,lEP,aVMem,mVMem,lVMem,VMemF,EPf,aPMem,mPMem,lPMem,aNproc,mNproc,lNproc,PMemF,NprocF,aIO,mIO,lIO,aIOPS,mIOPS,lIOPS,CPUf,IOf";
    my $lveid   = getuid();
    my $period  = !exists $CFG{'period'} || $CFG{'period'} eq '' ? '1d' : $CFG{'period'};
    my @command = ( '/usr/sbin/lveinfo', '--csv', "--period=$period", "--id=$lveid", "--style=user" );
    if ( lvestats_ver() > 1 ) {
        push @command, ("--compat=v2" , "--limit=0", "--show-columns=".$show_columns_v2);
    }
    my $INPUT   = CloudLinux::safeRun( @command );
    my @results = split( /\n+/, $INPUT );
    shift @results;

    foreach my $i (@results) {
        my ( $from, $to, $acpu, $mcpu, $lcpu, $aep, $mep, $lep, $aMem, $mMem, $lMem, $memf, $mepf,
             $aPmem, $mPmem, $lPmem, $aNproc, $mNproc, $lNproc, $pmemf, $nprocf, $aIO, $mIO, $lIO,
             $aIOPS, $mIOPS, $lIOPS, $cpuf,  $IOf ) = split( /,/, $i );
        my ( $apcpu, $mpcpu ) = ( 0, 0 );
        $lcpu  = 100 if ( $lcpu == 0 );
        $apcpu = Cpanel::Math::ceil( $acpu * 100 / $lcpu );
        $mpcpu = Cpanel::Math::ceil( $mcpu * 100 / $lcpu );
        my $stack = {
            'from'    => $from,
            'to'      => $to,
            'acpu'    => $acpu,
            'mcpu'    => $mcpu,
            'lcpu'    => $lcpu,
            'aep'     => $aep,
            'mep'     => $mep,
            'lep'     => $lep,
            'aMem'    => $aMem,
            'mMem'    => $mMem,
            'lMem'    => $lMem,
            'aPMem'   => $aPmem,
            'mPMem'   => $mPmem,
            'lPMem'   => $lPmem,
            'aNproc'  => $aNproc,
            'mNproc'  => $mNproc,
            'lNproc'  => $lNproc,
            'aIO'     => $aIO,
            'mIO'  => $mIO,
            'lIO'  => $lIO,
            'cpuf'    => $cpuf,
            'memf'    => $memf,
            'pmemf'    => $pmemf,
            'mepf'    => $mepf,
            'nprocf'  => $nprocf,
            'IOf'     => $IOf,
            'aIOPS'   => $aIOPS,
            'mIOPS'   => $mIOPS,
            'lIOPS'   => $lIOPS,
            'apcpu'   => $apcpu,
            'mpcpu'   => $mpcpu,
            'aMemStr' => RoundMemB($aMem),
            'mMemStr' => RoundMemB($mMem),
            'lMemStr' => RoundMemB($lMem),
            'eapcpu'  => isError( $apcpu > 75 ),
            'empcpu'  => isError( $mpcpu > 90 ),
            'eaep'    => isError( ( $lep > 0 ) && ( $aep * 2 > $lep ) ),
            'emep'    => isError( ( $lep > 0 ) && ( $mep > $lep * .9 ) ),
            'eaMem'   => isError( ( $lMem > 0 ) && ( $aMem * 2 > $lMem ) ),
            'emMem'   => isError( ( $lMem > 0 ) && ( $mMem > $lMem * .9 ) ),
            'ememf'   => isError( $memf > 0 ),
            'emepf'   => isError( $mepf > 0 )
        };
    # filtering the data actual for OpenVZ
    if ($inside_openvz) {
        $stack = filter_dict($stack, 'from|to|aep|mep|lep|mepf|pmemf|eaep|emep|emepf');
        }
        unshift(@RSD, $stack);
    }
    return \@RSD;
}

sub api2 {
    my ($func) = @_;
    my %API = (
       'getUsage' => {
           'func'   => 'api2_getUsage',
            'engine' => 'hasharray',
        },
       'getUsage2' => {
           'func'   => 'api2_getUsage2',
            'engine' => 'hasharray',
        },
        getSummary  => {
            'func'  => 'api2_getSummary',
            'engine'=> 'hasharray',
        },
        processPHPVersionSelect  => {
            'func'  => 'api2_processPHPVersionSelect',
            'engine'=> 'hasharray',
        },
        showPHPExtTable  => {
            'func'  => 'api2_showPHPExtTable',
            'engine'=> 'hasharray',
        },
        showPHPOptTable  => {
            'func'  => 'api2_showPHPOptTable',
            'engine'=> 'hasharray',
        },
        hideIfNative  => {
            'func'  => 'api2_hideIfNative',
            'engine'=> 'hasharray',
        },
        snapshot_list => {
            'func' => 'api2_snapshot_list',
            'engine'=> 'hasharray',
        },
        snapshot_one => {
            'func' => 'api2_snapshot_one',
            'engine'=> 'hasharray',
        },
        rubySelector  => {
            'func'  => 'api2_rubySelector',
            'engine'=> 'hasharray',
        },
        pythonSelector  => {
            'func'  => 'api2_pythonSelector',
            'engine'=> 'hasharray',
        },
        getRubyInterpreters  => {
            'func'  => 'api2_getRubyInterpreters',
            'engine'=> 'hasharray',
        },
        isRubyEnabled => {
            'func'  => "api2_isRubyEnabled",
            'engine' => 'hasharray',
        },
        getPythonInterpreters  => {
            'func'  => 'api2_getPythonInterpreters',
            'engine'=> 'hasharray',
        },
        getLvemanagerVersion  => {
            'func'  => 'api2_getLvemanagerVersion',
            'engine'=> 'hasharray',
        },
        getHomeDir  => {
            'func'  => 'api2_getHomeDir',
            'engine'=> 'hasharray',
        },
    );
    return \%{ $API{$func} };
}

sub get_user_domain {
    my $path = shift;
    my $user = getpwuid($<);
    my $full_path = qq#$path/users/$user#;
    my $dns;
    return unless -e $full_path;
    open my $f, '<', $full_path or return;
    while (my $line = <$f>) {
        if ($line =~ /^DNS\s?=\s?(\S*)/) {
            $dns = $1;
            last;
        }
    }
    close $f;
    return $dns;
}

sub api2_getRubyInterpreters {
    my %opts = @_;
    my @params = ('/usr/bin/selectorctl',
        '--interpreter', 'ruby',
        '--list',
        '--json');
    my $rv = CloudLinux::safeRun(@params);
    #return [map +{version=>$_}, keys %{Cpanel::JSON::Load($rv)}];
    my %data = %{Cpanel::JSON::Load($rv)};
    return $data{status} eq 'OK' ? [map +{version=>$_}, sort {$b cmp $a} keys %{$data{data}}] : [];
}

sub api2_isRubyEnabled {
    return not has_new_config_value('hideRubyApp', 'uiSettings');
}

sub api2_getLvemanagerVersion {
    return CloudLinux::safeRun('cat /usr/share/l.v.e-manager/version');
}

sub api2_getPythonInterpreters {
    my %opts = @_;
    my @params = ('/usr/bin/selectorctl',
        '--interpreter', 'python',
        '--list',
        '--json');
    my $rv = CloudLinux::safeRun(@params);
    my %data = %{Cpanel::JSON::Load($rv)};
    return $data{status} eq 'OK' ? [map +{version=>$_}, sort keys %{$data{data}}] : [];
}

sub api2_rubySelector {
    my %opts = @_;
    my $user = getpwuid($<);
    my $versions = api2_getRubyInterpreters();
    my @params = ('/usr/bin/selectorctl', '--interpreter', 'ruby', '--user', $user, '--user-summary', '--json');
    my $rv = CloudLinux::safeRun(@params);
    my $process = sub {
        my $data = shift;
        my $rv = [];
        if (exists $data->{status} and $data->{status} eq 'ERROR') {
            return [{status=>'ERROR', message=>$data->{message}}];
        }
        elsif (exists $data->{status} and $data->{status} eq 'OK') {
            for my $k ( keys %{ $data->{data} } ) {
                my $domain = get_user_domain('/var/cpanel');
                $data->{data}->{$k}->{domain} = substr($domain, -1) ne '/'
                    ? $domain . '/'
                    : $domain unless exists $data->{data}->{$k}->{domain};
                push @{ $rv }, {name=>$k, data=>$data->{data}->{$k}, versions=>$versions};
            }
        }
        return $rv;
    };
    return $process->(Cpanel::JSON::Load($rv));
}

sub api2_pythonSelector {
    my %opts = @_;
    my $user = getpwuid($<);
    my $versions = api2_getPythonInterpreters();
    my @params = ('/usr/bin/selectorctl', '--interpreter', 'python', '--user', $user, '--user-summary', '--json');
    my $rv = CloudLinux::safeRun(@params);
    my $process = sub {
        my $data = shift;
        my $rv = [];
        if (exists $data->{status} and $data->{status} eq 'ERROR') {
            return [{status=>'ERROR', message=>$data->{message}}];
        }
        elsif (exists $data->{status} and $data->{status} eq 'OK') {
            for my $k ( keys %{ $data->{data} } ) {
                my $domain = get_user_domain('/var/cpanel');
                $data->{data}->{$k}->{domain} = substr($domain, -1) ne '/'
                    ? $domain . '/'
                    : $domain unless exists $data->{data}->{$k}->{domain};
                push @{ $rv }, {name=>$k, data=>$data->{data}->{$k}, versions=>$versions};
            }
        }
        return $rv;
    };
    return $process->(Cpanel::JSON::Load($rv));
}

sub api2_processPHPVersionSelect {
    $locale ||= Cpanel::Locale->get_handle();
    # returns alternatives state for a user
    # and applies alternatives selection if
    # the form is submitted
    unless (check_license()) {
        return [ { errmsg => $locale->maketext('PHP Selector disabled, please contact hoster') } ]
    }
    # get parameters from calling side
    my %opts = @_;

    # check correct php version
    if (exists $opts{lveversion} && $opts{lveversion} !~ m/(^(\d\.\d)?( \(.*\))?$)|(^native \(\d\.\d\)$)/ ) {
        $data = { errmsg => "ERROR: php version is incorrect" };
        my $native = get_native_version();
        $data->{native} = $native if $native ne q{};
        return [ $data ];
    };

    # get current user name
    my $user = scalar getpwuid($<);

    # default command string to display summary
    my $param_string = qq#/usr/bin/selectorctl --user-summary --interpreter=php --user=$user --show-native-version 2>&1|#;

    # if form has been submitted, we first apply settings
    # and then show summary
    if ( $opts{lveversion} and $opts{lveversion} ne 'none' ) {
        my $version = strip_version($opts{lveversion});
        $param_string = qq#/usr/bin/selectorctl --interpreter=php --set-user-current=$version --user=$user --print-summary --show-native-version 2>&1|#;
    }

    # open pipe for command and grab output to string
    open $p, $param_string;
    my $raw_string = do { local $/; <$p> };
    close $p;

    my @options;
    # if returned string starts with 'ERROR', something wrong has happened
    # Strip 'ERROR' word and send error string to be displayed
    if ( index( $raw_string, 'ERROR' ) >= 0 ) {
        $raw_string =~ s/^.*ERROR:(.*?)\n/$1/s;
        $data = { errmsg => $raw_string };
        my $native = get_native_version();
        $data->{native} = $native if $native ne q{};
        return [ $data ];
    }
    elsif ( $raw_string =~ s/WARN:(.*?)\n// ) {
        push @options, { warnmsg => $1 };
    }
    # if no error then parse returned string into array of arrays
    my @raw_options = map { [ split(/\s+/,$_,5) ] } split( /\n/, $raw_string );

    # data is returned in the following form for entry: 'version e d s'
    # 'e' stands for enabled, 'd' for default and 's' for selected.
    # opposite value is marked with a hyphen ('-')
    # process already structured data taking into account
    # if entry is marked as 'selected' or 'disabled'

    for my $i ( @raw_options ) {
        my $ver = $i->[0];
        my $state = $i->[1] ne 'e'
            ? q#disabled#
            : $i->[3] eq 's'
                ? q#selected#
                : ''
                ;
        my $name_modifier = $i->[4];

        # and save'em in array
        push @options, { state=>$state, ver=>$ver, name_modifier=>$name_modifier };
    }
    return \@options;
}

sub api2_showPHPExtTable {
    # returns extension table
    unless (check_license()) {
        return []
    }
    # get current user
    my $user = scalar getpwuid($<);

    # open pipe for command and grab output to string
    open my $p, qq#/usr/bin/selectorctl --list-user-extensions --interpreter=php --user=$user --all 2>&1|#;
    my $raw_string = do { local $/; <$p> };

    # if returned string starts with 'ERROR', something wrong has happened
    # We are not about to show anything if error
    if ( index( $raw_string, 'ERROR' ) == 0 or index( $raw_string, 'WARN' ) == 0) {
        return [];
    }
    my $data = eval `/usr/bin/cl-phpextdesc --perl`;
    $data = [] unless ref $data eq 'ARRAY';
    my %data_desc = map { $_->{title} => $_->{description} } @{ $data };

    # if no error then parse returned string into array of hashes
    my @ext =
        sort { $a->{title} cmp $b->{title} }
            map {
                my ( $s, $n ) = split;
                { title => $n,
                  state => ( $s eq '+' ? 1 : 0 ),
                  desc  => ( $data_desc{$n} || q{} ) };
            } split( /\n/, $raw_string );
    return \@ext;
}

sub api2_showPHPOptTable {
    # returns options table
    unless (check_license()) {
        return []
    }
    # get current user
    my $user = scalar getpwuid($<);

    # open pipe for command and grab output to string
    my $data = eval `/usr/bin/selectorctl --print-options-safe --user=$user --perl`;
    return [] unless ref $data eq 'ARRAY';
    return $data;
}

sub api2_snapshot_list {
    # get current user
    my $user = scalar getpwuid($<);
    my %opts = @_;
    # open pipe for command and grab output to string
    my $command = "/usr/sbin/lve-read-snapshot --from $opts{'from'} --to $opts{'to'} --user=$user --list --json";
    my $data = `$command`;
    my $decoded = JSON::XS::decode_json($data);
    return $decoded;
}

sub api2_snapshot_one {
    # get current user
    my $user = scalar getpwuid($<);
    my %opts = @_;
    # open pipe for command and grab output to string
    my $command = "/usr/sbin/lve-read-snapshot --timestamp $opts{'timestamp'} --user=$user --json";
    my $data = `$command`;
    my $decoded = JSON::XS::decode_json($data);
    return $decoded;
}

sub api2_getHomeDir {
    my @data = getpwuid($<);
    return $data[7];
}

sub LVEInfo_PHPExtDesc {
    # print titles for extensions (for javascript)
    open my $p,qq#/usr/bin/cl-phpextdesc --json|#;
    print do { local $/; <$p>; };
    close $p;
}

sub get_usage_info {
    my $res = api2_getUsage2( 'period' => '1d' );
    shift @$res;  # remove version number in first element
    return $res;
}

sub check_license {
    my @params = ('/usr/bin/cldetect', '--check-license');
    my $result = CloudLinux::safeRun(@params);
    chomp $result;
    if ($result eq 'OK') {
        return 1;
    }
    return 0;
}

sub LVEInfo_print_usage_overview {
    $locale ||= Cpanel::Locale->get_handle();
    unless (check_license()) {
        print '<strong>'.$locale->maketext('Resource Usage monitoring disabled, please contact hoster').'</strong>';
        return;
    }

    my $usage = get_usage_info();
    my $snapshot_link;
    my $lvestats_ver_ = lvestats_ver();
    if ( $lvestats_ver_ > 1 ) {
        $snapshot_link =  q# or <a href="snapshots/index.html">[#.$locale->maketext('Snapshots').q#]</a>#;
    }
    unless (@$usage) {
        print '<h3>'.$locale->maketext('There has been no activity on your site within the past 24 hours').'</h3><br/>';
        print q#<p><a href="resourceusagedetails.html">[#.$locale->maketext('Details').q#]</a>#.$snapshot_link.q#</p>#;
        return;
    }

    my $cpu_limited_flag = 0;
    my $io_limited_flag  = 0;
    my $iops_limited_flag = 0;
    my $cpuf_total       = 0;
    my $memf_total       = 0;
    my $mepf_total       = 0;
    my $iof_total        = 0;
    my $iopsf_total      = 0;
    my $cpu_max          = 0;
    my $mem_max          = 0;
    my $lmem_max         = 0;
    my $ep_max           = 0;
    my $lep_max          = 0;
    my $pmem_max         = 0;
    my $lpmem_max        = 0;
    my $pmemf_total      = 0;
    my $nproc_max        = 0;
    my $lnproc_max       = 0;
    my $nprocf_total     = 0;
    my $iousage_max      = 0;
    my $iolimit_max      = 0;
    my $iopsusage_max    = 0;
    my $iopslimit_max    = 0;
    foreach my $hash (@$usage) {
        # use this algorithm detecting limits overflow for old lvestats
        $cpu_limited_flag = 1 if ( $lvestats_ver_ <= 1 ) && ( $hash->{'mCPU'} >= $hash->{'lCPU'} );
        $io_limited_flag = 1 if ( $lvestats_ver_ <= 1 ) && ( $hash->{'lIO'} && $hash->{'mIO'} >= $hash->{'lIO'} );
        $iops_limited_flag = 1 if ( $lvestats_ver_ <= 1 ) && ( $hash->{'lIOPS'} && $hash->{'mIOPS'} >= $hash->{'lIOPS'} );
        $cpuf_total   += $hash->{'CPUf'};
        $memf_total   += $hash->{'VMemF'};
        $mepf_total   += $hash->{'EPf'};
        $pmemf_total  += $hash->{'PMemF'};
        $nprocf_total += $hash->{'NprocF'};
        $iof_total    += $hash->{'IOf'};
        $iopsf_total  += $hash->{'IOPSf'};
        $cpu_max    = $hash->{'mCPU'  } if ( $hash->{'mCPU'  } > $cpu_max );
        $mem_max    = $hash->{'mVMem' } if ( $hash->{'mVMem' } > $mem_max );
        $lmem_max   = $hash->{'lVMem' } if ( $hash->{'lVMem' } > $lmem_max );
        $ep_max     = $hash->{'mEP'   } if ( $hash->{'mEP'   } > $ep_max );
        $lep_max    = $hash->{'lEP'   } if ( $hash->{'lEP'   } > $lep_max );
        $lpmem_max  = $hash->{'lPMem' } if ( $hash->{'lPMem' } > $lpmem_max );
        $pmem_max   = $hash->{'mPMem' } if ( $hash->{'mPMem' } > $pmem_max );
        $nproc_max  = $hash->{'mNproc'} if ( $hash->{'mNproc'} > $nproc_max );
        $lnproc_max = $hash->{'lNproc'} if ( $hash->{'lNproc'} > $lnproc_max );
        $iousage_max = $hash->{'mIO'} if ( $hash->{'mIO'} > $iousage_max );
        $iolimit_max = $hash->{'lIO'} if ( $hash->{'lIO'} > $iolimit_max );
        $iopsusage_max = $hash->{'mIOPS'} if ( $hash->{'mIOPS'} > $iopsusage_max );
        $iopslimit_max = $hash->{'lIOPS'} if ( $hash->{'lIOPS'} > $iopslimit_max );
    }

    if ( ($cpu_limited_flag || $cpuf_total) || ( $memf_total > 0 ) || ( $pmemf_total > 0 ) || ( $mepf_total > 0 ) || ( $nprocf_total > 0 ) || ($io_limited_flag || $iof_total > 0) || ($iops_limited_flag || $iopsf_total > 0) ) {
        print '<h3>'.$locale->maketext('Your site has been limited within the past 24 hours').'</h3>';

        if ($cpu_limited_flag || $cpuf_total) {
            print '<p>'.$locale->maketext('CPU resources were limited for your site');
        }

        if ( $memf_total > 0 ) {
            print '<p>'.$locale->maketext('Virtual memory resources were limited for your site');
        }

        if ( $pmemf_total > 0 ) {
            print '<p>'.$locale->maketext('Physical memory resources were limited for your site');
        }

        if ( $mepf_total > 0 ) {
            print '<p>'.$locale->maketext('You have reached entry processes (number of simultaneously running php and cgi scripts, as well as cron jobs and shell sessions) limit <b>[_1]</b> times', $mepf_total);
        }

        if ( $nprocf_total > 0 ) {
            print '<p>'.$locale->maketext('You have reached processes number limit <b>[_1]</b> times', $nprocf_total);
        }

        if ( $io_limited_flag || $iof_total > 0 ) {
            print '<p>'.$locale->maketext('I/O usage resources were limited for your site');
        }

        if ( $iops_limited_flag || $iopsf_total > 0 ) {
            print '<p>'.$locale->maketext('I/O operations per second were limited for your site');
        }

    }
    else {
        print '<h3>'.$locale->maketext('Your site had no issues in the past 24 hours').'</h3>';
    }

    my $cpu_warn = (!$cpu_limited_flag) && ($cpu_max > 90) && !$cpuf_total;
    my $mem_warn = ( $memf_total == 0 ) && ( $lmem_max > 0 ) && ( $mem_max > $lmem_max * 0.9 );
    my $pmem_warn = ( $pmemf_total == 0 ) && ( $lpmem_max > 0 ) && ( $pmem_max > $lpmem_max * 0.9 );
    my $mep_warn = ( $mepf_total == 0 ) && ( $lep_max > 0 )  && ( $ep_max > $lep_max * 0.9 );
    my $nproc_warn = ( $nprocf_total == 0 ) && ( $lnproc_max > 0 )  && ( $nproc_max > $lnproc_max * 0.9 );
    my $io_warn = ( $iolimit_max > 0 ) && (!$io_limited_flag) && ( $iousage_max > $iolimit_max * 0.9 ) && !$iof_total ;
    my $iops_warn = ( $iopslimit_max > 0 ) && (!$iops_limited_flag) && ( $iopsusage_max > $iopslimit_max * 0.9 ) && !$iopsf_total ;
    if ( $cpu_warn || $mem_warn || $pmem_warn || $mep_warn || $nproc_warn || $io_warn || $iops_warn)
    {
        print '<p><h3>'.$locale->maketext('Your site might hit resource limits soon').'</h3>';

        if ( $cpu_warn ) {
            print '<p>'.$locale->maketext('Your CPU usage was at [_1]% out of 100%', $cpu_max);
        }

        if ( $mem_warn ) {
            print '<p>'.$locale->maketext('Your Virtual Memory usage was at [_1] out of [_2]', RoundMemB($mem_max), RoundMemB($lmem_max));
        }

        if ( $pmem_warn ) {
            print '<p>'.$locale->maketext('Your Physical Memory usage was at [_1] out of [_2]', RoundMemB($pmem_max), $lpmem_max);
        }

        if ( $mep_warn ) {
            print '<p>'.$locale->maketext('You had [_1] entry processes (number of simultaneously running php and cgi scripts, as well as cron jobs and shell sessions) out of [_2] max entry processes allowed', $ep_max, $lep_max);
        }

        if ( $nproc_warn ) {
            print '<p>'.$locale->maketext('You had [_1] processes out of [_2] max processes allowed', $nproc_max, $lnproc_max);
        }

        if ( $io_warn ) {
            print '<p>'.$locale->maketext('You had [_1] I/O usage out of [_2] max I/O usage allowed', $iousage_max, $iolimit_max);
        }

        if ( $iops_warn ) {
            print '<p>'.$locale->maketext('You had [_1] I/O operations per second out of [_2] max I/O operations per second allowed', $iopsusage_max, $iopslimit_max);
        }
    }
    if ( $lvestats_ver_ > 1 ) {
        $snapshot_link =  q# #.$locale->maketext('or').q# <a href="snapshots/index.html">[#.$locale->maketext('Snapshots').q#]</a>#;
    }
    print q#<p><a href="resourceusagedetails.html">[#.$locale->maketext('Details').q#]</a>#.$snapshot_link.q#</p>#;
}

# Function converts bytes to kilobytes, megabytes or gigabytes (if needed)
# and appends appropriate suffix (K, M, G)
# Parameter $_[1] = memory in bytes
sub RoundMemB {
    my $mem = shift;

    if ( $mem >= ( 1024 * 1024 * 1024 ) ) {
        return sprintf( "%.1fG", $mem / ( 1024 * 1024 * 1024 ) );
    }

    if ( $mem >= 1024 * 1024 ) {
        return sprintf( "%.1fM", $mem / ( 1024 * 1024 ) );
    }

    if ( $mem >= 1024 ) {
        return sprintf( "%.0fK", $mem / 1024 );
    }

    return "$mem";
}

# Function converts kilobytes to megabytes or gigabytes (if needed)
# and appends appropriate suffix (K, M, G)
# Parameter $_[1] = memory in kilobytes
sub RoundMemK {
    my $mem = shift;

    if ( $mem >= ( 1024 * 1024 ) ) {
        return sprintf( "%.1fG", $mem / ( 1024 * 1024 ) );
    }

    if ( $mem >= 1024 ) {
        return sprintf( "%.1fM", $mem / 1024 );
    }

    return "$mem" . 'K';
}

# check
sub has_config_value {
    my ($key) = @_;
    return 0 if !$key;

    my $cpanel_config = '/var/cpanel/cpanel.config';
    return 0 if ! -e $cpanel_config;

    # precompile regexp for efficiency
    my $key_pattern = qr/^$key\s*=\s*1\s*$/;

    my $enabled = 0;
    open my $f, '<', $cpanel_config or return 0;
    while (my $line = <$f>) {
        if ($line =~ /$key_pattern/) {
            $enabled = 1;
            last;
        }
    }
    close $f;
    return $enabled;
}

# check new config file
sub has_new_config_value {
    my ($key, $parent_key) = (@_);
    return 0 if !$key or !$parent_key;
    my $json;
    my $status = 0;
    if (open my $fh, "<", "/usr/share/l.v.e-manager/lvemanager-config.json") {
        $json = <$fh>;
        $status = JSON::XS::decode_json($json)->{'ui_config'}->{$parent_key}->{$key};
    }
    return $status;
}

sub k_to_m {
    my ( $val ) = @_;
    my $rv = $val / 1024;
    my $fmt = ($rv == int $rv) ? "%d" : "%.1f";
    return sprintf $fmt, $rv;
}

sub get_native_version {
    my $version = q{};
    my $open_string = qq#/usr/bin/selectorctl --show-native-version|#;
    eval {
        open $p, $open_string;
        $version = do { local $/; <$p>; };
        chomp $version;
        close $p;
    };
    return $version if $version =~ /\d\.\d+/;
    return q{};
}

sub strip_version {
    # stripts parens part e.g. '(5.3)' from version string
    my ($version) = @_;
    my $pos = index($version, '(');
    return $version if $pos == -1;
    return substr($version, 0, $pos);
}

# function to filter dict per keys, using regexp
sub filter_dict {
    my ( $dict_rel, $regexp ) = @_;
    my %dict = %$dict_rel;
    return map {$_=>$dict{$_}} (grep {$_ =~ /$regexp/} keys %dict)
}

# return integer lvestats version
sub lvestats_ver {
    if (-e '/usr/sbin/lve-read-snapshot') {
       return 2
    };
    return 1
}

sub is_normalize_user_cpu {
    # by default - TRUE
    open CONFIG_FILE, '</etc/sysconfig/cloudlinux' || return 1;
    while (<CONFIG_FILE>) {
        chomp;
        if (/normalized_user_cpu[ ]*=[ ]*(Y|N)/is) {
            return (uc($1) eq 'Y') ? 1 : 0;
        }
    }
    return 1;
}

sub percent {
    my ($value, $max) = @_;
    $value = int($value);
    $max = int($max);
    return ($max == 0 ? 0 : int($value / $max * 100));
}

1;
