From c04a319479d16068aa557e4074ece22f18f44d6c Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 09:23:15 -0500 Subject: [PATCH 01/20] add various HP specific bits for identity info --- snmp/smart-v1 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 9a42e175b..0eab14d5d 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -476,7 +476,8 @@ foreach my $line (@disks) { # get the drive serial number, if needed my $disk_id = $name; $output=`$smartctl -i $disk`; - while ( $output =~ /(?i)Serial Number:(.*)/g ) { + # generally upper case, HP branded drives seem to report with lower case n + while ( $output =~ /(?i)Serial [Nn]umber:(.*)/g ) { $IDs{'serial'} = $1; $IDs{'serial'} =~ s/^\s+|\s+$//g; } @@ -504,6 +505,22 @@ foreach my $line (@disks) { $IDs{'fw_version'} =~ s/^\s+|\s+$//g; } + # mainly HP drives using this + while ( $output =~ /(?i)Vendor:(.*)/g ) { + $IDs{'vendor'} = $1; + $IDs{'vendor'} =~ s/^\s+|\s+$//g; + } + + while ( $output =~ /(?i)Product:(.*)/g ) { + $IDs{'vendor'} = $1; + $IDs{'vendor'} =~ s/^\s+|\s+$//g; + } + + while ( $output =~ /(?i)Revision:(.*)/g ) { + $IDs{'vendor'} = $1; + $IDs{'vendor'} =~ s/^\s+|\s+$//g; + } + $output = `$smartctl -H $disk`; if ( $output =~ /SMART\ overall\-health\ self\-assessment\ test\ result\:\ PASSED/ ) { $IDs{'health_pass'} = 1; From f6863ba98741d56fd399c954bf729cb76cb733ff Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 09:41:14 -0500 Subject: [PATCH 02/20] more HP related cleanup --- snmp/smart-v1 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 0eab14d5d..defe0ce28 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -477,7 +477,7 @@ foreach my $line (@disks) { my $disk_id = $name; $output=`$smartctl -i $disk`; # generally upper case, HP branded drives seem to report with lower case n - while ( $output =~ /(?i)Serial [Nn]umber:(.*)/g ) { + while ( $output =~ /(?i)Serial Number:(.*)/g ) { $IDs{'serial'} = $1; $IDs{'serial'} =~ s/^\s+|\s+$//g; } @@ -512,18 +512,20 @@ foreach my $line (@disks) { } while ( $output =~ /(?i)Product:(.*)/g ) { - $IDs{'vendor'} = $1; - $IDs{'vendor'} =~ s/^\s+|\s+$//g; + $IDs{'product'} = $1; + $IDs{'product'} =~ s/^\s+|\s+$//g; } while ( $output =~ /(?i)Revision:(.*)/g ) { - $IDs{'vendor'} = $1; - $IDs{'vendor'} =~ s/^\s+|\s+$//g; + $IDs{'revision'} = $1; + $IDs{'revision'} =~ s/^\s+|\s+$//g; } $output = `$smartctl -H $disk`; if ( $output =~ /SMART\ overall\-health\ self\-assessment\ test\ result\:\ PASSED/ ) { $IDs{'health_pass'} = 1; + }elsif ($output =~ /SMART\ Health\ Status\:\ OK/) { + $IDs{'health_pass'} = 1; } $to_return->{data}{disks}{$disk_id} = \%IDs; From f1759fd80c52b61d3fe7e2eb8b09a9408bc55230 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 10:30:26 -0500 Subject: [PATCH 03/20] add initial ccis guessing support --- snmp/smart-v1 | 74 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index defe0ce28..eaddba718 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -85,23 +85,34 @@ my $useSN = 1; $Getopt::Std::STANDARD_HELP_VERSION = 1; sub main::VERSION_MESSAGE { - print "SMART SNMP extend 0.1.0\n"; + print "SMART SNMP extend 0.2.0\n"; } sub main::HELP_MESSAGE { + &VERSION_MESSAGE; print "\n" . "-u Update '" . $cache . "'\n" . "-g Guess at the config and print it to STDOUT.\n" . "-c The config file to use.\n" . "-p Pretty print the JSON.\n" - . "-Z GZip+Base64 compress the results.\n"; + . "-Z GZip+Base64 compress the results.\n" + . "-C Enable manual checking for guess and ccis.\n"; } ## end sub main::HELP_MESSAGE #gets the options my %opts = (); -getopts( 'ugc:pZ', \%opts ); +getopts( 'ugc:pZhv', \%opts ); + +if ( $opts{h} ) { + &HELP_MESSAGE; + exit; +} +if ( $opts{v} ) { + &VERSION_MESSAGE; + exit; +} # configure JSON for later usage my $json = JSON->new->allow_nonref->canonical(1); @@ -196,6 +207,53 @@ if ( defined( $opts{g} ) ) { } ## end foreach my $arguments (@argumentsA) + # handle /dev/sg0 if found + # + # ignoring /dev/sg1 as on the systems I have to test on, + # it will always be present and the same as sg0 + # - Zane + if ( -e '/dev/sg0' && $^O eq 'linux' ) { + my $ccis_vol_status = `which ccis_vol_status 2> /dev/null`; + if ( $? != 0 && !$opts{C} ) { + $drive_lines + = $drive_lines + . "# -C not given, but /dev/sg0 exists and ccis_vol_status is not present\n" + . "# skipping checking for ccis devices for SMART polling\n"; + } elsif ( $? != 0 && $opts{C} ) { + my $drive_count = 0; + my $continue = 1; + while ($continue) { + my $output = `$smartctl -A /dev/sg0 -d ccis,$drive_count 2> /dev/null`; + if ( $? != 0 ) { + $continue = 0; + } else { + $continue = 0; + while ( $output =~ /(?i)START OF READ SMART DATA SECTION(.*)/g && !$continue ) { + $continue = 1; + } + if ($continue) { + $drive_lines + = $drive_lines . 'sg0-' . $drive_count . ' /dev/sg0 -d ccis,' . $drive_count . "\n"; + } + } ## end else [ if ( $? != 0 ) ] + $drive_count++; + } ## end while ($continue) + } else { + my $output = `$ccis_vol_status -V /dev/sg0`; + my $drive_count = 0; + while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { + $1 =~ s/^\s+|\s+$//g; + $drive_count += $1; + } + my $drive_int = 0; + while ( $drive_int < $drive_count ) { + $drive_lines = $drive_lines . 'sg0-' . $drive_int . ' /dev/sg0 -d ccis,' . $drive_int . "\n"; + + $drive_int++; + } + } ## end else [ if ( $? != 0 && !$opts{C} ) ] + } ## end if ( -e '/dev/sg0' && $^O eq 'linux' ) + print "useSN=1\n" . 'smartctl=' . $smartctl . "\n" . $cache . $drive_lines; exit 0; @@ -409,7 +467,6 @@ foreach my $line (@disks) { # Elements in Grown Defect List. # Marking as 5 Reallocated_Sector_Ct - if ( $line =~ "Elements in grown defect list:" ) { my @lineA = split( /\ /, $line, 10 ); @@ -422,7 +479,6 @@ foreach my $line (@disks) { # Current Drive Temperature # Marking as 194 Temperature_Celsius - if ( $line =~ "Current Drive Temperature:" ) { my @lineA = split( /\ /, $line, 10 ); @@ -475,7 +531,7 @@ foreach my $line (@disks) { # get the drive serial number, if needed my $disk_id = $name; - $output=`$smartctl -i $disk`; + $output = `$smartctl -i $disk`; # generally upper case, HP branded drives seem to report with lower case n while ( $output =~ /(?i)Serial Number:(.*)/g ) { $IDs{'serial'} = $1; @@ -524,7 +580,7 @@ foreach my $line (@disks) { $output = `$smartctl -H $disk`; if ( $output =~ /SMART\ overall\-health\ self\-assessment\ test\ result\:\ PASSED/ ) { $IDs{'health_pass'} = 1; - }elsif ($output =~ /SMART\ Health\ Status\:\ OK/) { + } elsif ( $output =~ /SMART\ Health\ Status\:\ OK/ ) { $IDs{'health_pass'} = 1; } @@ -538,12 +594,12 @@ if ( !$opts{p} ) { $toReturn = $toReturn . "\n"; } -if ($opts{Z}) { +if ( $opts{Z} ) { my $compressed = encode_base64( gzip($toReturn) ); $compressed =~ s/\n//g; $compressed = $compressed . "\n"; if ( length($compressed) < length($toReturn) ) { - $toReturn=$compressed; + $toReturn = $compressed; } } From 723daaefe5d51e9bd62edad1cf27775eafeed251 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 10:37:14 -0500 Subject: [PATCH 04/20] ccis -> cciss --- snmp/smart-v1 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index eaddba718..0dce9e064 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -97,13 +97,13 @@ sub main::HELP_MESSAGE { . "-c The config file to use.\n" . "-p Pretty print the JSON.\n" . "-Z GZip+Base64 compress the results.\n" - . "-C Enable manual checking for guess and ccis.\n"; + . "-C Enable manual checking for guess and cciss.\n"; } ## end sub main::HELP_MESSAGE #gets the options my %opts = (); -getopts( 'ugc:pZhv', \%opts ); +getopts( 'ugc:pZhvC', \%opts ); if ( $opts{h} ) { &HELP_MESSAGE; @@ -213,17 +213,17 @@ if ( defined( $opts{g} ) ) { # it will always be present and the same as sg0 # - Zane if ( -e '/dev/sg0' && $^O eq 'linux' ) { - my $ccis_vol_status = `which ccis_vol_status 2> /dev/null`; + my $cciss_vol_status = `which cciss_vol_status 2> /dev/null`; if ( $? != 0 && !$opts{C} ) { $drive_lines = $drive_lines - . "# -C not given, but /dev/sg0 exists and ccis_vol_status is not present\n" - . "# skipping checking for ccis devices for SMART polling\n"; + . "# -C not given, but /dev/sg0 exists and cciss_vol_status is not present\n" + . "# skipping checking for cciss devices for SMART polling\n"; } elsif ( $? != 0 && $opts{C} ) { my $drive_count = 0; my $continue = 1; while ($continue) { - my $output = `$smartctl -A /dev/sg0 -d ccis,$drive_count 2> /dev/null`; + my $output = `$smartctl -A /dev/sg0 -d cciss,$drive_count 2> /dev/null`; if ( $? != 0 ) { $continue = 0; } else { @@ -233,13 +233,13 @@ if ( defined( $opts{g} ) ) { } if ($continue) { $drive_lines - = $drive_lines . 'sg0-' . $drive_count . ' /dev/sg0 -d ccis,' . $drive_count . "\n"; + = $drive_lines . 'sg0-' . $drive_count . ' /dev/sg0 -d cciss,' . $drive_count . "\n"; } } ## end else [ if ( $? != 0 ) ] $drive_count++; } ## end while ($continue) } else { - my $output = `$ccis_vol_status -V /dev/sg0`; + my $output = `$cciss_vol_status -V /dev/sg0`; my $drive_count = 0; while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { $1 =~ s/^\s+|\s+$//g; @@ -247,7 +247,7 @@ if ( defined( $opts{g} ) ) { } my $drive_int = 0; while ( $drive_int < $drive_count ) { - $drive_lines = $drive_lines . 'sg0-' . $drive_int . ' /dev/sg0 -d ccis,' . $drive_int . "\n"; + $drive_lines = $drive_lines . 'sg0-' . $drive_int . ' /dev/sg0 -d cciss,' . $drive_int . "\n"; $drive_int++; } From 41c1a0e5db2d010e4d9e799e66afe0b7b07fd622 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 10:51:37 -0500 Subject: [PATCH 05/20] rework cciss support some more --- snmp/smart-v1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 0dce9e064..1999df81e 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -213,12 +213,12 @@ if ( defined( $opts{g} ) ) { # it will always be present and the same as sg0 # - Zane if ( -e '/dev/sg0' && $^O eq 'linux' ) { - my $cciss_vol_status = `which cciss_vol_status 2> /dev/null`; + my $output = 'cciss_vol_status -V /dev/sg0'; if ( $? != 0 && !$opts{C} ) { $drive_lines = $drive_lines . "# -C not given, but /dev/sg0 exists and cciss_vol_status is not present\n" - . "# skipping checking for cciss devices for SMART polling\n"; + . "# in path or 'ccis_vol_status -V /dev/sg0' is failing\n"; } elsif ( $? != 0 && $opts{C} ) { my $drive_count = 0; my $continue = 1; @@ -239,11 +239,11 @@ if ( defined( $opts{g} ) ) { $drive_count++; } ## end while ($continue) } else { - my $output = `$cciss_vol_status -V /dev/sg0`; my $drive_count = 0; while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { - $1 =~ s/^\s+|\s+$//g; - $drive_count += $1; + my $drive_count_line=$1; + $drive_count_line =~ s/^\s+|\s+$//g; + $drive_count += $drive_count_line; } my $drive_int = 0; while ( $drive_int < $drive_count ) { From eb00eca5a6bf512ad090e6b4eed5146f5732d827 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 10:57:03 -0500 Subject: [PATCH 06/20] derp, fix qoute type --- snmp/smart-v1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 1999df81e..9b9de62e1 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -213,7 +213,7 @@ if ( defined( $opts{g} ) ) { # it will always be present and the same as sg0 # - Zane if ( -e '/dev/sg0' && $^O eq 'linux' ) { - my $output = 'cciss_vol_status -V /dev/sg0'; + my $output = `cciss_vol_status -V /dev/sg0`; if ( $? != 0 && !$opts{C} ) { $drive_lines = $drive_lines From 9b3ebb15f8b984334c92fd839285a94795f6d750 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 11:04:17 -0500 Subject: [PATCH 07/20] make useSN configuration with -g --- snmp/smart-v1 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 9b9de62e1..55f2d1f32 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -97,13 +97,14 @@ sub main::HELP_MESSAGE { . "-c The config file to use.\n" . "-p Pretty print the JSON.\n" . "-Z GZip+Base64 compress the results.\n" - . "-C Enable manual checking for guess and cciss.\n"; + . "-C Enable manual checking for guess and cciss.\n" + . "-S Set useSN to 0 when using -g\n"; } ## end sub main::HELP_MESSAGE #gets the options my %opts = (); -getopts( 'ugc:pZhvC', \%opts ); +getopts( 'ugc:pZhvCS', \%opts ); if ( $opts{h} ) { &HELP_MESSAGE; @@ -254,7 +255,12 @@ if ( defined( $opts{g} ) ) { } ## end else [ if ( $? != 0 && !$opts{C} ) ] } ## end if ( -e '/dev/sg0' && $^O eq 'linux' ) - print "useSN=1\n" . 'smartctl=' . $smartctl . "\n" . $cache . $drive_lines; + my $useSN=1; + if ($opts{S}) { + $useSN=0; + } + + print 'useSN='.$useSN."\n" . 'smartctl=' . $smartctl . "\n" . $cache . $drive_lines; exit 0; } ## end if ( defined( $opts{g} ) ) From 96469d8aef3bca8063e4a40dda4b711cae12bc63 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 11:12:11 -0500 Subject: [PATCH 08/20] rework self test logs to be more HP friendly --- snmp/smart-v1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 55f2d1f32..45fe37e9c 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -504,7 +504,7 @@ foreach my $line (@disks) { #get the selftest logs $output = `$smartctl -l selftest $disk`; @outputA = split( /\n/, $output ); - my @completed = grep( /Completed without error/, @outputA ); + my @completed = grep( /Completed/, @outputA ); $IDs{'completed'} = scalar @completed; my @interrupted = grep( /Interrupted/, @outputA ); $IDs{'interrupted'} = scalar @interrupted; @@ -512,13 +512,13 @@ foreach my $line (@disks) { $IDs{'read_failure'} = scalar @read_failure; my @unknown_failure = grep( /unknown failure/, @outputA ); $IDs{'unknown_failure'} = scalar @unknown_failure; - my @extended = grep( /Extended/, @outputA ); + my @extended = grep( /[Ee]xtended|Long/, @outputA ); $IDs{'extended'} = scalar @extended; - my @short = grep( /Short/, @outputA ); + my @short = grep( /[Ss]hort/, @outputA ); $IDs{'short'} = scalar @short; - my @conveyance = grep( /Conveyance/, @outputA ); + my @conveyance = grep( /[Cc]onveyance/, @outputA ); $IDs{'conveyance'} = scalar @conveyance; - my @selective = grep( /Selective/, @outputA ); + my @selective = grep( /[Ss]elective/, @outputA ); $IDs{'selective'} = scalar @selective; # if we have logs, actually grab the log output From ce28e61a44d14cf4b802962283e2e555556a7d5a Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 11:37:35 -0500 Subject: [PATCH 09/20] more test cleanup --- snmp/smart-v1 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 45fe37e9c..62c0fabed 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -242,7 +242,7 @@ if ( defined( $opts{g} ) ) { } else { my $drive_count = 0; while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { - my $drive_count_line=$1; + my $drive_count_line = $1; $drive_count_line =~ s/^\s+|\s+$//g; $drive_count += $drive_count_line; } @@ -255,12 +255,12 @@ if ( defined( $opts{g} ) ) { } ## end else [ if ( $? != 0 && !$opts{C} ) ] } ## end if ( -e '/dev/sg0' && $^O eq 'linux' ) - my $useSN=1; - if ($opts{S}) { - $useSN=0; + my $useSN = 1; + if ( $opts{S} ) { + $useSN = 0; } - print 'useSN='.$useSN."\n" . 'smartctl=' . $smartctl . "\n" . $cache . $drive_lines; + print 'useSN=' . $useSN . "\n" . 'smartctl=' . $smartctl . "\n" . $cache . $drive_lines; exit 0; } ## end if ( defined( $opts{g} ) ) @@ -520,6 +520,8 @@ foreach my $line (@disks) { $IDs{'conveyance'} = scalar @conveyance; my @selective = grep( /[Ss]elective/, @outputA ); $IDs{'selective'} = scalar @selective; + my @offline = grep( /(\d|[Bb]ackground|[Ff]oreground)+\ +[Oo]ffline/, @outputA ); + $IDs{'offline'} = scalar @offline; # if we have logs, actually grab the log output if ( $IDs{'completed'} > 0 @@ -528,11 +530,14 @@ foreach my $line (@disks) { || $IDs{'extended'} > 0 || $IDs{'short'} > 0 || $IDs{'conveyance'} > 0 - || $IDs{'selective'} > 0 ) + || $IDs{'selective'} > 0 + || $IDs{'offline'} > 0 ) { + my @headers = grep( /(Num\ +Test.*LBA| Description .*[Hh]ours)/, @outputA ); + my @log_lines; - push( @log_lines, @extended, @short, @conveyance, @selective ); - $IDs{'selftest_log'} = join( "\n", sort(@log_lines) ); + push( @log_lines, @extended, @short, @conveyance, @selective, @offline ); + $IDs{'selftest_log'} = join( "\n", @headers, sort(@log_lines) ); } ## end if ( $IDs{'completed'} > 0 || $IDs{'interrupted'...}) # get the drive serial number, if needed From 7d385ca4eb5e41ac48b591371b62b67dc54b8391 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 11:43:57 -0500 Subject: [PATCH 10/20] more test cleanup --- snmp/smart-v1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 62c0fabed..80da78bc2 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -512,7 +512,7 @@ foreach my $line (@disks) { $IDs{'read_failure'} = scalar @read_failure; my @unknown_failure = grep( /unknown failure/, @outputA ); $IDs{'unknown_failure'} = scalar @unknown_failure; - my @extended = grep( /[Ee]xtended|Long/, @outputA ); + my @extended = grep( /\d.*([Ee]xtended|\ [Ll]ong)/, @outputA ); $IDs{'extended'} = scalar @extended; my @short = grep( /[Ss]hort/, @outputA ); $IDs{'short'} = scalar @short; From 0ed437a18ec184a5ff5922f5df0a12a9d03b97c3 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 11:59:23 -0500 Subject: [PATCH 11/20] finally get the extended test playing nice with HP stuff --- snmp/smart-v1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 80da78bc2..235a93010 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -512,7 +512,7 @@ foreach my $line (@disks) { $IDs{'read_failure'} = scalar @read_failure; my @unknown_failure = grep( /unknown failure/, @outputA ); $IDs{'unknown_failure'} = scalar @unknown_failure; - my @extended = grep( /\d.*([Ee]xtended|\ [Ll]ong)/, @outputA ); + my @extended = grep( /\d.*\ ([Ee]xtended|[Ll]ong).*(?![Dd]uration)/, @outputA ); $IDs{'extended'} = scalar @extended; my @short = grep( /[Ss]hort/, @outputA ); $IDs{'short'} = scalar @short; From 49780f9fafc2b4c9a366248d21053f7b58d53669 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 12:10:37 -0500 Subject: [PATCH 12/20] don't print needless error messages if cciss_vol_status is not found --- snmp/smart-v1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 235a93010..8a84355b2 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -214,7 +214,7 @@ if ( defined( $opts{g} ) ) { # it will always be present and the same as sg0 # - Zane if ( -e '/dev/sg0' && $^O eq 'linux' ) { - my $output = `cciss_vol_status -V /dev/sg0`; + my $output = `cciss_vol_status -V /dev/sg0 2> /dev/null`; if ( $? != 0 && !$opts{C} ) { $drive_lines = $drive_lines From dee42bc5b10580b9ef7908ff1bcdcf6b2b944434 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 20:41:08 -0500 Subject: [PATCH 13/20] cleanup a edge case, add a new edge case, and now find the max temp --- snmp/smart-v1 | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 8a84355b2..6033783b3 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -369,6 +369,7 @@ foreach my $line (@disks) { 'serial' => undef, 'selftest_log' => undef, 'health_pass' => 0, + max_temp => 'null', ); $IDs{'disk'} =~ s/^\/dev\///; @@ -412,7 +413,9 @@ foreach my $line (@disks) { # Crucial SSD # 202, Percent_Lifetime_Remain, same as 231, SSD Life Left - if ( $id == 202 ) { + if ( $id == 202 + && $line =~ /Percent_Lifetime_Remain/ ) + { $IDs{231} = $raw; } @@ -434,11 +437,25 @@ foreach my $line (@disks) { # single int normalized values if ( ( $id == 177 ) + || ( $id == 230 ) || ( $id == 231 ) || ( $id == 233 ) ) { - $IDs{$id} = int($normalized); - } + # WDC WDS500G2B0A + # 230 Media_Wearout_Indicator 0x0032 100 100 --- Old_age Always - 0x002e000a002e + if ( $id == 230 + && $line =~ /Media_Wearout_Indicator/ ) + { + $IDs{233} = int($normalized); + } else { + # only set 233 if it has not been set yet + # if it was set already then the above did it and we don't want + # to overwrite it + if ( $id == 233 && $IDs{233} eq "null" ) { + $IDs{$id} = int($normalized); + } + } + } ## end if ( ( $id == 177 ) || ( $id == 230 ) || (...)) # 9, power on hours if ( $id == 9 ) { @@ -588,6 +605,16 @@ foreach my $line (@disks) { $IDs{'revision'} =~ s/^\s+|\s+$//g; } + # figure out what to use for the max temp, if there is one + if ($IDs{'190'} =~ /^\d+$/) { + $IDs{max_temp}=$IDs{'190'}; + }elsif ($IDs{'194'} =~ /^\d+$/) { + $IDs{max_temp}=$IDs{'194'}; + } + if ($IDs{'194'} =~ /^\d+$/ && defined($IDs{max_temp}) && $IDs{'194'} > $IDs{max_temp}) { + $IDs{max_temp}=$IDs{'194'}; + } + $output = `$smartctl -H $disk`; if ( $output =~ /SMART\ overall\-health\ self\-assessment\ test\ result\:\ PASSED/ ) { $IDs{'health_pass'} = 1; From 67fe2ac073baca1808e7ed1607fc94eea4229857 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Tue, 20 Jun 2023 22:21:59 -0500 Subject: [PATCH 14/20] add id 232 --- snmp/smart-v1 | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 6033783b3..60271abd8 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -363,6 +363,7 @@ foreach my $line (@disks) { '198' => 'null', '199' => 'null', '231' => 'null', + '232' => 'null', '233' => 'null', '9' => 'null', 'disk' => $disk, @@ -439,22 +440,33 @@ foreach my $line (@disks) { if ( ( $id == 177 ) || ( $id == 230 ) || ( $id == 231 ) + || ( $id == 232 ) || ( $id == 233 ) ) { + # annoying non-standard disk # WDC WDS500G2B0A # 230 Media_Wearout_Indicator 0x0032 100 100 --- Old_age Always - 0x002e000a002e + # 232 Available_Reservd_Space 0x0033 100 100 004 Pre-fail Always - 100 + # 233 NAND_GB_Written_TLC 0x0032 100 100 --- Old_age Always - 9816 + if ( $id == 230 && $line =~ /Media_Wearout_Indicator/ ) { $IDs{233} = int($normalized); + } elsif ( $id == 232 + && $line =~ /Available_Reservd_Space/ ) + { + $IDs{232} = int($normalized); } else { # only set 233 if it has not been set yet # if it was set already then the above did it and we don't want # to overwrite it if ( $id == 233 && $IDs{233} eq "null" ) { $IDs{$id} = int($normalized); + } elsif ( $id != 233 ) { + $IDs{$id} = int($normalized); } - } + } ## end else [ if ( $id == 230 && $line =~ /Media_Wearout_Indicator/)] } ## end if ( ( $id == 177 ) || ( $id == 230 ) || (...)) # 9, power on hours @@ -606,13 +618,13 @@ foreach my $line (@disks) { } # figure out what to use for the max temp, if there is one - if ($IDs{'190'} =~ /^\d+$/) { - $IDs{max_temp}=$IDs{'190'}; - }elsif ($IDs{'194'} =~ /^\d+$/) { - $IDs{max_temp}=$IDs{'194'}; + if ( $IDs{'190'} =~ /^\d+$/ ) { + $IDs{max_temp} = $IDs{'190'}; + } elsif ( $IDs{'194'} =~ /^\d+$/ ) { + $IDs{max_temp} = $IDs{'194'}; } - if ($IDs{'194'} =~ /^\d+$/ && defined($IDs{max_temp}) && $IDs{'194'} > $IDs{max_temp}) { - $IDs{max_temp}=$IDs{'194'}; + if ( $IDs{'194'} =~ /^\d+$/ && defined( $IDs{max_temp} ) && $IDs{'194'} > $IDs{max_temp} ) { + $IDs{max_temp} = $IDs{'194'}; } $output = `$smartctl -H $disk`; From f5320b3148156789dedbdbbea2650f1f07ecedfd Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Fri, 23 Jun 2023 13:22:40 -0500 Subject: [PATCH 15/20] make the scan modes selectable and begin reworking cciss forproperly checking all possible devices --- snmp/smart-v1 | 322 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 214 insertions(+), 108 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 60271abd8..695d82512 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -90,21 +90,31 @@ sub main::VERSION_MESSAGE { sub main::HELP_MESSAGE { &VERSION_MESSAGE; - print "\n" - . "-u Update '" - . $cache . "'\n" - . "-g Guess at the config and print it to STDOUT.\n" - . "-c The config file to use.\n" - . "-p Pretty print the JSON.\n" - . "-Z GZip+Base64 compress the results.\n" - . "-C Enable manual checking for guess and cciss.\n" - . "-S Set useSN to 0 when using -g\n"; + print "\n" . "-u Update '" . $cache . "'\n" . '-g Guess at the config and print it to STDOUT +-c The config file to use. +-p Pretty print the JSON. +-Z GZip+Base64 compress the results. +-C Enable manual checking for guess and cciss. +-S Set useSN to 0 when using -g +-G Guess modes to use. This is a comma seperated list. + Default :: scan-open,cciss-vol-status + +Scan Modes: + +- scan :: Use "--scan" with smartctl. "scan-open" will take presidence. + +- scan-open :: Call smartctl with "--scan-open". + +- cciss-vol-status :: Freebsd/Linux specific and if it sees /dev/sg0(on Linux) or + /dev/ciss0(on FreebSD) it will attempt to find drives via cciss-vol-status, + falling if that is not found to checking for disks via smrtctl. +'; } ## end sub main::HELP_MESSAGE #gets the options my %opts = (); -getopts( 'ugc:pZhvCS', \%opts ); +getopts( 'ugc:pZhvCSG', \%opts ); if ( $opts{h} ) { &HELP_MESSAGE; @@ -115,10 +125,37 @@ if ( $opts{v} ) { exit; } +# +# figure out what scan modes to use if -g specified +# +my $scan_modes = { + 'scan-open' => 0, + 'scan' => 0, + 'cciss_vol_status' => 0, +}; +if ( $opts{g} ) { + if ( !defined( $opts{G} ) ) { + $opts{G} = 'scan-open,cciss_vol_status'; + } + $opts{G} =~ s/[\ \t]//g; + my @scan_modes_split = split( /,/, $opts{G} ); + foreach my $mode (@scan_modes_split) { + if ( !defined $scan_modes->{$mode} ) { + die( '"' . $mode . '" is not a recognized scan mode' ); + } + $scan_modes->{$mode} = 1; + } +} ## end if ( $opts{g} ) + # configure JSON for later usage -my $json = JSON->new->allow_nonref->canonical(1); -if ( $opts{p} ) { - $json->pretty; +# only need to do this if actually running as in -g is not specified +my $json; +if ( !$opts{g} ) { + + $json = JSON->new->allow_nonref->canonical(1); + if ( $opts{p} ) { + $json->pretty; + } } my $to_return = { @@ -148,119 +185,188 @@ if ( defined( $opts{g} ) ) { $cache = 'cache=' . $cache . "\n"; } - # used for checking if a disk has been found more than once - my %found_disks_names; - my @argumentsA; - - #have smartctl scan and see if it finds anythings not get found - my $scan_output = `$smartctl --scan-open`; - my @scan_outputA = split( /\n/, $scan_output ); - - # remove non-SMART devices sometimes returned - @scan_outputA = grep( !/ses[0-9]/, @scan_outputA ); # not a disk, but may or may not have SMART attributes - @scan_outputA = grep( !/pass[0-9]/, @scan_outputA ); # very likely a duplicate and a disk under another name - @scan_outputA = grep( !/cd[0-9]/, @scan_outputA ); # CD drive - if ( $^O eq 'freebsd' ) { - @scan_outputA = grep( !/sa[0-9]/, @scan_outputA ); # tape drive - @scan_outputA = grep( !/ctl[0-9]/, @scan_outputA ); # CAM target layer - } elsif ( $^O eq 'linux' ) { - @scan_outputA = grep( !/st[0-9]/, @scan_outputA ); # SCSI tape drive - @scan_outputA = grep( !/ht[0-9]/, @scan_outputA ); # ATA tape drive - } + my $drive_lines = ''; - # make the first pass, figuring out what all we have and trimming comments - foreach my $arguments (@scan_outputA) { - my $name = $arguments; - - $arguments =~ s/ \#.*//; # trim the comment out of the argument - $name =~ s/ .*//; - $name =~ s/\/dev\///; - if ( defined( $found_disks_names{$name} ) ) { - $found_disks_names{$name}++; - } else { - $found_disks_names{$name} = 0; + # + # + # scan-open and scan guess mode handling + # + # + if ( $scan_modes->{'scan-open'} || $scan_modes->{'scan'} ) { + # used for checking if a disk has been found more than once + my %found_disks_names; + my @argumentsA; + + # use scan-open if it is set, overriding scan if it is also set + my $mode = 'scan'; + if ( $scan_modes->{'scan-open'} ) { + $mode = 'scan-open'; } - push( @argumentsA, $arguments ); + #have smartctl scan and see if it finds anythings not get found + my $scan_output = `$smartctl --$mode`; + my @scan_outputA = split( /\n/, $scan_output ); + + # remove non-SMART devices sometimes returned + @scan_outputA = grep( !/ses[0-9]/, @scan_outputA ); # not a disk, but may or may not have SMART attributes + @scan_outputA = grep( !/pass[0-9]/, @scan_outputA ); # very likely a duplicate and a disk under another name + @scan_outputA = grep( !/cd[0-9]/, @scan_outputA ); # CD drive + if ( $^O eq 'freebsd' ) { + @scan_outputA = grep( !/sa[0-9]/, @scan_outputA ); # tape drive + @scan_outputA = grep( !/ctl[0-9]/, @scan_outputA ); # CAM target layer + } elsif ( $^O eq 'linux' ) { + @scan_outputA = grep( !/st[0-9]/, @scan_outputA ); # SCSI tape drive + @scan_outputA = grep( !/ht[0-9]/, @scan_outputA ); # ATA tape drive + } - } ## end foreach my $arguments (@scan_outputA) + # make the first pass, figuring out what all we have and trimming comments + foreach my $arguments (@scan_outputA) { + my $name = $arguments; - # second pass, putting the lines together - my %current_disk; - my $drive_lines = ''; - foreach my $arguments (@argumentsA) { - my $name = $arguments; - $name =~ s/ .*//; - $name =~ s/\/dev\///; - - if ( $found_disks_names{$name} == 0 ) { - # If no other devices, just name it after the base device. - $drive_lines = $drive_lines . $name . " " . $arguments . "\n"; - } else { - # if more than one, start at zero and increment, apennding comma number to the base device name - if ( defined( $current_disk{$name} ) ) { - $current_disk{$name}++; + $arguments =~ s/ \#.*//; # trim the comment out of the argument + $name =~ s/ .*//; + $name =~ s/\/dev\///; + if ( defined( $found_disks_names{$name} ) ) { + $found_disks_names{$name}++; } else { - $current_disk{$name} = 0; + $found_disks_names{$name} = 0; } - $drive_lines = $drive_lines . $name . "," . $current_disk{$name} . " " . $arguments . "\n"; - } - } ## end foreach my $arguments (@argumentsA) + push( @argumentsA, $arguments ); - # handle /dev/sg0 if found - # - # ignoring /dev/sg1 as on the systems I have to test on, - # it will always be present and the same as sg0 - # - Zane - if ( -e '/dev/sg0' && $^O eq 'linux' ) { - my $output = `cciss_vol_status -V /dev/sg0 2> /dev/null`; - if ( $? != 0 && !$opts{C} ) { - $drive_lines - = $drive_lines - . "# -C not given, but /dev/sg0 exists and cciss_vol_status is not present\n" - . "# in path or 'ccis_vol_status -V /dev/sg0' is failing\n"; - } elsif ( $? != 0 && $opts{C} ) { - my $drive_count = 0; - my $continue = 1; - while ($continue) { - my $output = `$smartctl -A /dev/sg0 -d cciss,$drive_count 2> /dev/null`; - if ( $? != 0 ) { - $continue = 0; + } ## end foreach my $arguments (@scan_outputA) + + # second pass, putting the lines together + my %current_disk; + foreach my $arguments (@argumentsA) { + my $name = $arguments; + $name =~ s/ .*//; + $name =~ s/\/dev\///; + + if ( $found_disks_names{$name} == 0 ) { + # If no other devices, just name it after the base device. + $drive_lines = $drive_lines . $name . " " . $arguments . "\n"; + } else { + # if more than one, start at zero and increment, apennding comma number to the base device name + if ( defined( $current_disk{$name} ) ) { + $current_disk{$name}++; } else { - $continue = 0; - while ( $output =~ /(?i)START OF READ SMART DATA SECTION(.*)/g && !$continue ) { - $continue = 1; - } - if ($continue) { - $drive_lines - = $drive_lines . 'sg0-' . $drive_count . ' /dev/sg0 -d cciss,' . $drive_count . "\n"; - } - } ## end else [ if ( $? != 0 ) ] - $drive_count++; - } ## end while ($continue) - } else { - my $drive_count = 0; - while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { - my $drive_count_line = $1; - $drive_count_line =~ s/^\s+|\s+$//g; - $drive_count += $drive_count_line; + $current_disk{$name} = 0; + } + $drive_lines = $drive_lines . $name . "," . $current_disk{$name} . " " . $arguments . "\n"; } - my $drive_int = 0; - while ( $drive_int < $drive_count ) { - $drive_lines = $drive_lines . 'sg0-' . $drive_int . ' /dev/sg0 -d cciss,' . $drive_int . "\n"; - $drive_int++; - } - } ## end else [ if ( $? != 0 && !$opts{C} ) ] - } ## end if ( -e '/dev/sg0' && $^O eq 'linux' ) + } ## end foreach my $arguments (@argumentsA) + } ## end if ( $scan_modes->{'scan-open'} || $scan_modes...) + + # + # + # scan mode handler for cciss_vol_status + # /dev/sg* devices for cciss on Linux + # /dev/ccis* devices for cciss on FreeBSD + # + # + if ( $scan_modes->{'cciss_vol_status'} && ( $^O eq 'linux' || $^O eq 'freebsd' ) ) { + my $cciss; + if ( $^O eq 'freebsd' ) { + $cciss = '/dev/ciss'; + } elsif ( $^O eq 'linux' ) { + $cciss = '/dev/sg'; + } + + my $sg_process = 1; + if ( -e '/dev/' . $cciss . '0' ) { + my $device = '/dev/' . $cciss . '0'; + my $output = `cciss_vol_status -V $device 2> /dev/null`; + if ( $? != 0 && !$opts{C} ) { + $sg_process = 0; + $drive_lines + = $drive_lines + . "# -C not given, but " + . $device + . " exists and cciss_vol_status is not present\n" + . "# in path or 'ccis_vol_status -V " + . $device + . "' is failing\n"; + } ## end if ( $? != 0 && !$opts{C} ) + } ## end if ( -e '/dev/' . $cciss . '0' ) + my $sg_int = 0; + my $seen_lines = {}; + my $device = '/dev/' . $cciss . $sg_int; + while ( -e $device && $sg_process ) { + my $output = `cciss_vol_status -V $device 2> /dev/null`; + if ( $? != 0 && !$opts{C} ) { + # just empty here as we just want to skip it if it fails and there is no C + # warning is above + } elsif ( $? != 0 && $opts{C} ) { + my $drive_count = 0; + my $continue = 1; + while ($continue) { + my $output = `$smartctl -A $device -d cciss,$drive_count 2> /dev/null`; + if ( $? != 0 ) { + $continue = 0; + } else { + $continue = 0; + my $add_it = 0; + # if we have smart data for this device, process it + while ( $output =~ /(?i)START OF READ SMART DATA SECTION(.*)/g && !$continue ) { + $continue = 1; + my $id; + while ( $output =~ /(?i)Serial Number:(.*)/g ) { + $id = $1; + $id =~ s/^\s+|\s+$//g; + } + if ( defined($id) && !defined( $seen_lines->{$id} ) ) { + $add_it = 1; + $seen_lines->{$id} = 1; + } + } ## end while ( $output =~ /(?i)START OF READ SMART DATA SECTION(.*)/g...) + if ( $continue && $add_it ) { + $drive_lines + = $drive_lines + . $cciss . '0-' + . $drive_count . ' ' + . $device + . ' -d cciss,' + . $drive_count . "\n"; + } + } ## end else [ if ( $? != 0 ) ] + $drive_count++; + } ## end while ($continue) + } else { + my $drive_count = 0; + while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { + my $drive_count_line = $1; + $drive_count_line =~ s/^\s+|\s+$//g; + $drive_count += $drive_count_line; + } + my $drive_int = 0; + while ( $drive_int < $drive_count ) { + $drive_lines + = $drive_lines . $cciss . '0-' . $drive_int . ' ' . $device . ' -d cciss,' . $drive_int . "\n"; + + $drive_int++; + } + } ## end else [ if ( $? != 0 && !$opts{C} ) ] + + $sg_int++; + $device = '/dev/' . $cciss . $sg_int; + } ## end while ( -e $device && $sg_process ) + } ## end if ( $scan_modes->{'linux_cciss_vol_status'...}) my $useSN = 1; if ( $opts{S} ) { $useSN = 0; } - print 'useSN=' . $useSN . "\n" . 'smartctl=' . $smartctl . "\n" . $cache . $drive_lines; + print '# scan_modes=' + . $opts{G} + . "\nuseSN=" + . $useSN . "\n" + . 'smartctl=' + . $smartctl . "\n" + . $cache + . $drive_lines; exit 0; } ## end if ( defined( $opts{g} ) ) From 4a6cff86ab89a50e7636f683843f0d243a4b456b Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Sun, 25 Jun 2023 01:37:36 -0500 Subject: [PATCH 16/20] rework how the cciss device path is generated --- snmp/smart-v1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 695d82512..443f259c0 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -274,9 +274,12 @@ if ( defined( $opts{g} ) ) { $cciss = '/dev/sg'; } + # generate the initial device path that will be checked + my $sg_int = 0; + my $device = $cciss . $sg_int; + my $sg_process = 1; - if ( -e '/dev/' . $cciss . '0' ) { - my $device = '/dev/' . $cciss . '0'; + if ( -e $device ) { my $output = `cciss_vol_status -V $device 2> /dev/null`; if ( $? != 0 && !$opts{C} ) { $sg_process = 0; @@ -289,10 +292,8 @@ if ( defined( $opts{g} ) ) { . $device . "' is failing\n"; } ## end if ( $? != 0 && !$opts{C} ) - } ## end if ( -e '/dev/' . $cciss . '0' ) - my $sg_int = 0; + } ## end if ( -e $device ) my $seen_lines = {}; - my $device = '/dev/' . $cciss . $sg_int; while ( -e $device && $sg_process ) { my $output = `cciss_vol_status -V $device 2> /dev/null`; if ( $? != 0 && !$opts{C} ) { @@ -350,9 +351,9 @@ if ( defined( $opts{g} ) ) { } ## end else [ if ( $? != 0 && !$opts{C} ) ] $sg_int++; - $device = '/dev/' . $cciss . $sg_int; + $device = $cciss . $sg_int; } ## end while ( -e $device && $sg_process ) - } ## end if ( $scan_modes->{'linux_cciss_vol_status'...}) + } ## end if ( $scan_modes->{'cciss_vol_status'} && ...) my $useSN = 1; if ( $opts{S} ) { From ae2695d3f47ad23430bb85bcb77fc8360adc5f0a Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Sun, 25 Jun 2023 17:19:27 -0500 Subject: [PATCH 17/20] add exit status checking --- snmp/smart-v1 | 497 ++++++++++++++++++++++++++------------------------ 1 file changed, 262 insertions(+), 235 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 443f259c0..5dc985530 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -159,13 +159,17 @@ if ( !$opts{g} ) { } my $to_return = { - data => { disks => {} }, + data => { disks => {}, exit_nonzero => 0, unhealthy => 0, }, version => 1, error => 0, errorString => '', }; +# +# # guess if asked +# +# if ( defined( $opts{g} ) ) { #get what path to use for smartctl @@ -384,7 +388,11 @@ open( my $readfh, "<", $config ) or die "Can't open '" . $config . "'"; read( $readfh, $config_file, 1000000 ); close($readfh); -#parse the config file and remove comments and empty lines +# +# +# parse the config file and remove comments and empty lines +# +# my @configA = split( /\n/, $config_file ); @configA = grep( !/^$/, @configA ); @configA = grep( !/^\#/, @configA ); @@ -440,6 +448,11 @@ if ( !defined( $opts{u} ) ) { } } ## end if ( !defined( $opts{u} ) ) +# +# +# Process each disk +# +# foreach my $line (@disks) { my $disk; my $name; @@ -449,12 +462,11 @@ foreach my $line (@disks) { $disk = $line; $name = $line; } - my $output; if ( $disk !~ /\// ) { $disk = '/dev/' . $disk; } - $output = `$smartctl -A $disk`; - my %IDs = ( + my $output = `$smartctl -A $disk`; + my %IDs = ( '5' => 'null', '10' => 'null', '173' => 'null', @@ -478,271 +490,286 @@ foreach my $line (@disks) { 'selftest_log' => undef, 'health_pass' => 0, max_temp => 'null', + exit => $?, ); $IDs{'disk'} =~ s/^\/dev\///; - my @outputA; - - if ( $output =~ /NVMe Log/ ) { - # we have an NVMe drive with annoyingly different output - my %mappings = ( - 'Temperature' => 194, - 'Power Cycles' => 12, - 'Power On Hours' => 9, - 'Percentage Used' => 231, - ); - foreach ( split( /\n/, $output ) ) { - if (/:/) { - my ( $key, $val ) = split(/:/); - $val =~ s/^\s+|\s+$|\D+//g; - if ( exists( $mappings{$key} ) ) { - if ( $mappings{$key} == 231 ) { - $IDs{ $mappings{$key} } = 100 - $val; - } else { - $IDs{ $mappings{$key} } = $val; - } - } - } ## end if (/:/) - } ## end foreach ( split( /\n/, $output ) ) - + # if polling exited non-zero above, no reason running the rest of the checks + my $disk_id = $name; + if ( $IDs{exit} != 0 ) { + $to_return->{data}{exit_nonzero}++; } else { - @outputA = split( /\n/, $output ); - my $outputAint = 0; - while ( defined( $outputA[$outputAint] ) ) { - my $line = $outputA[$outputAint]; - $line =~ s/^ +//; - $line =~ s/ +/ /g; - - if ( $line =~ /^[0123456789]+ / ) { - my @lineA = split( /\ /, $line, 10 ); - my $raw = $lineA[9]; - my $normalized = $lineA[3]; - my $id = $lineA[0]; - - # Crucial SSD - # 202, Percent_Lifetime_Remain, same as 231, SSD Life Left - if ( $id == 202 - && $line =~ /Percent_Lifetime_Remain/ ) - { - $IDs{231} = $raw; - } + my @outputA; + + if ( $output =~ /NVMe Log/ ) { + # we have an NVMe drive with annoyingly different output + my %mappings = ( + 'Temperature' => 194, + 'Power Cycles' => 12, + 'Power On Hours' => 9, + 'Percentage Used' => 231, + ); + foreach ( split( /\n/, $output ) ) { + if (/:/) { + my ( $key, $val ) = split(/:/); + $val =~ s/^\s+|\s+$|\D+//g; + if ( exists( $mappings{$key} ) ) { + if ( $mappings{$key} == 231 ) { + $IDs{ $mappings{$key} } = 100 - $val; + } else { + $IDs{ $mappings{$key} } = $val; + } + } + } ## end if (/:/) + } ## end foreach ( split( /\n/, $output ) ) + + } else { + @outputA = split( /\n/, $output ); + my $outputAint = 0; + while ( defined( $outputA[$outputAint] ) ) { + my $line = $outputA[$outputAint]; + $line =~ s/^ +//; + $line =~ s/ +/ /g; + + if ( $line =~ /^[0123456789]+ / ) { + my @lineA = split( /\ /, $line, 10 ); + my $raw = $lineA[9]; + my $normalized = $lineA[3]; + my $id = $lineA[0]; + + # Crucial SSD + # 202, Percent_Lifetime_Remain, same as 231, SSD Life Left + if ( $id == 202 + && $line =~ /Percent_Lifetime_Remain/ ) + { + $IDs{231} = $raw; + } - # single int raw values - if ( ( $id == 5 ) - || ( $id == 10 ) - || ( $id == 173 ) - || ( $id == 183 ) - || ( $id == 184 ) - || ( $id == 187 ) - || ( $id == 196 ) - || ( $id == 197 ) - || ( $id == 198 ) - || ( $id == 199 ) ) - { - my @rawA = split( /\ /, $raw ); - $IDs{$id} = $rawA[0]; - } ## end if ( ( $id == 5 ) || ( $id == 10 ) || ( $id...)) - - # single int normalized values - if ( ( $id == 177 ) - || ( $id == 230 ) - || ( $id == 231 ) - || ( $id == 232 ) - || ( $id == 233 ) ) - { + # single int raw values + if ( ( $id == 5 ) + || ( $id == 10 ) + || ( $id == 173 ) + || ( $id == 183 ) + || ( $id == 184 ) + || ( $id == 187 ) + || ( $id == 196 ) + || ( $id == 197 ) + || ( $id == 198 ) + || ( $id == 199 ) ) + { + my @rawA = split( /\ /, $raw ); + $IDs{$id} = $rawA[0]; + } ## end if ( ( $id == 5 ) || ( $id == 10 ) || ( $id...)) + + # single int normalized values + if ( ( $id == 177 ) + || ( $id == 230 ) + || ( $id == 231 ) + || ( $id == 232 ) + || ( $id == 233 ) ) + { # annoying non-standard disk # WDC WDS500G2B0A # 230 Media_Wearout_Indicator 0x0032 100 100 --- Old_age Always - 0x002e000a002e # 232 Available_Reservd_Space 0x0033 100 100 004 Pre-fail Always - 100 # 233 NAND_GB_Written_TLC 0x0032 100 100 --- Old_age Always - 9816 - if ( $id == 230 - && $line =~ /Media_Wearout_Indicator/ ) - { - $IDs{233} = int($normalized); - } elsif ( $id == 232 - && $line =~ /Available_Reservd_Space/ ) - { - $IDs{232} = int($normalized); - } else { - # only set 233 if it has not been set yet - # if it was set already then the above did it and we don't want - # to overwrite it - if ( $id == 233 && $IDs{233} eq "null" ) { - $IDs{$id} = int($normalized); - } elsif ( $id != 233 ) { - $IDs{$id} = int($normalized); - } - } ## end else [ if ( $id == 230 && $line =~ /Media_Wearout_Indicator/)] - } ## end if ( ( $id == 177 ) || ( $id == 230 ) || (...)) + if ( $id == 230 + && $line =~ /Media_Wearout_Indicator/ ) + { + $IDs{233} = int($normalized); + } elsif ( $id == 232 + && $line =~ /Available_Reservd_Space/ ) + { + $IDs{232} = int($normalized); + } else { + # only set 233 if it has not been set yet + # if it was set already then the above did it and we don't want + # to overwrite it + if ( $id == 233 && $IDs{233} eq "null" ) { + $IDs{$id} = int($normalized); + } elsif ( $id != 233 ) { + $IDs{$id} = int($normalized); + } + } ## end else [ if ( $id == 230 && $line =~ /Media_Wearout_Indicator/)] + } ## end if ( ( $id == 177 ) || ( $id == 230 ) || (...)) - # 9, power on hours - if ( $id == 9 ) { - my @runtime = split( /[\ h]/, $raw ); - $IDs{$id} = $runtime[0]; - } + # 9, power on hours + if ( $id == 9 ) { + my @runtime = split( /[\ h]/, $raw ); + $IDs{$id} = $runtime[0]; + } + + # 188, Command_Timeout + if ( $id == 188 ) { + my $total = 0; + my @rawA = split( /\ /, $raw ); + my $rawAint = 0; + while ( defined( $rawA[$rawAint] ) ) { + $total = $total + $rawA[$rawAint]; + $rawAint++; + } + $IDs{$id} = $total; + } ## end if ( $id == 188 ) - # 188, Command_Timeout - if ( $id == 188 ) { - my $total = 0; - my @rawA = split( /\ /, $raw ); - my $rawAint = 0; - while ( defined( $rawA[$rawAint] ) ) { - $total = $total + $rawA[$rawAint]; - $rawAint++; + # 190, airflow temp + # 194, temp + if ( ( $id == 190 ) + || ( $id == 194 ) ) + { + my ($temp) = split( /\ /, $raw ); + $IDs{$id} = $temp; } - $IDs{$id} = $total; - } ## end if ( $id == 188 ) - - # 190, airflow temp - # 194, temp - if ( ( $id == 190 ) - || ( $id == 194 ) ) - { - my ($temp) = split( /\ /, $raw ); - $IDs{$id} = $temp; - } - } ## end if ( $line =~ /^[0123456789]+ / ) + } ## end if ( $line =~ /^[0123456789]+ / ) - # SAS Wrapping - # Section by Cameron Munroe (munroenet[at]gmail.com) + # SAS Wrapping + # Section by Cameron Munroe (munroenet[at]gmail.com) - # Elements in Grown Defect List. - # Marking as 5 Reallocated_Sector_Ct - if ( $line =~ "Elements in grown defect list:" ) { + # Elements in Grown Defect List. + # Marking as 5 Reallocated_Sector_Ct + if ( $line =~ "Elements in grown defect list:" ) { - my @lineA = split( /\ /, $line, 10 ); - my $raw = $lineA[5]; + my @lineA = split( /\ /, $line, 10 ); + my $raw = $lineA[5]; - # Reallocated Sector Count ID - $IDs{5} = $raw; + # Reallocated Sector Count ID + $IDs{5} = $raw; - } + } - # Current Drive Temperature - # Marking as 194 Temperature_Celsius - if ( $line =~ "Current Drive Temperature:" ) { + # Current Drive Temperature + # Marking as 194 Temperature_Celsius + if ( $line =~ "Current Drive Temperature:" ) { - my @lineA = split( /\ /, $line, 10 ); - my $raw = $lineA[3]; + my @lineA = split( /\ /, $line, 10 ); + my $raw = $lineA[3]; - # Temperature C ID - $IDs{194} = $raw; + # Temperature C ID + $IDs{194} = $raw; - } + } - # End of SAS Wrapper - - $outputAint++; - } ## end while ( defined( $outputA[$outputAint] ) ) - } ## end else [ if ( $output =~ /NVMe Log/ ) ] - - #get the selftest logs - $output = `$smartctl -l selftest $disk`; - @outputA = split( /\n/, $output ); - my @completed = grep( /Completed/, @outputA ); - $IDs{'completed'} = scalar @completed; - my @interrupted = grep( /Interrupted/, @outputA ); - $IDs{'interrupted'} = scalar @interrupted; - my @read_failure = grep( /read failure/, @outputA ); - $IDs{'read_failure'} = scalar @read_failure; - my @unknown_failure = grep( /unknown failure/, @outputA ); - $IDs{'unknown_failure'} = scalar @unknown_failure; - my @extended = grep( /\d.*\ ([Ee]xtended|[Ll]ong).*(?![Dd]uration)/, @outputA ); - $IDs{'extended'} = scalar @extended; - my @short = grep( /[Ss]hort/, @outputA ); - $IDs{'short'} = scalar @short; - my @conveyance = grep( /[Cc]onveyance/, @outputA ); - $IDs{'conveyance'} = scalar @conveyance; - my @selective = grep( /[Ss]elective/, @outputA ); - $IDs{'selective'} = scalar @selective; - my @offline = grep( /(\d|[Bb]ackground|[Ff]oreground)+\ +[Oo]ffline/, @outputA ); - $IDs{'offline'} = scalar @offline; - - # if we have logs, actually grab the log output - if ( $IDs{'completed'} > 0 - || $IDs{'interrupted'} > 0 - || $IDs{'read_failure'} > 0 - || $IDs{'extended'} > 0 - || $IDs{'short'} > 0 - || $IDs{'conveyance'} > 0 - || $IDs{'selective'} > 0 - || $IDs{'offline'} > 0 ) - { - my @headers = grep( /(Num\ +Test.*LBA| Description .*[Hh]ours)/, @outputA ); - - my @log_lines; - push( @log_lines, @extended, @short, @conveyance, @selective, @offline ); - $IDs{'selftest_log'} = join( "\n", @headers, sort(@log_lines) ); - } ## end if ( $IDs{'completed'} > 0 || $IDs{'interrupted'...}) - - # get the drive serial number, if needed - my $disk_id = $name; - $output = `$smartctl -i $disk`; - # generally upper case, HP branded drives seem to report with lower case n - while ( $output =~ /(?i)Serial Number:(.*)/g ) { - $IDs{'serial'} = $1; - $IDs{'serial'} =~ s/^\s+|\s+$//g; - } - if ($useSN) { - $disk_id = $IDs{'serial'}; - } + # End of SAS Wrapper - while ( $output =~ /(?i)Model Family:(.*)/g ) { - $IDs{'model_family'} = $1; - $IDs{'model_family'} =~ s/^\s+|\s+$//g; - } + $outputAint++; + } ## end while ( defined( $outputA[$outputAint] ) ) + } ## end else [ if ( $output =~ /NVMe Log/ ) ] - while ( $output =~ /(?i)Device Model:(.*)/g ) { - $IDs{'device_model'} = $1; - $IDs{'device_model'} =~ s/^\s+|\s+$//g; - } + #get the selftest logs + $output = `$smartctl -l selftest $disk`; + @outputA = split( /\n/, $output ); + my @completed = grep( /Completed/, @outputA ); + $IDs{'completed'} = scalar @completed; + my @interrupted = grep( /Interrupted/, @outputA ); + $IDs{'interrupted'} = scalar @interrupted; + my @read_failure = grep( /read failure/, @outputA ); + $IDs{'read_failure'} = scalar @read_failure; + my @unknown_failure = grep( /unknown failure/, @outputA ); + $IDs{'unknown_failure'} = scalar @unknown_failure; + my @extended = grep( /\d.*\ ([Ee]xtended|[Ll]ong).*(?![Dd]uration)/, @outputA ); + $IDs{'extended'} = scalar @extended; + my @short = grep( /[Ss]hort/, @outputA ); + $IDs{'short'} = scalar @short; + my @conveyance = grep( /[Cc]onveyance/, @outputA ); + $IDs{'conveyance'} = scalar @conveyance; + my @selective = grep( /[Ss]elective/, @outputA ); + $IDs{'selective'} = scalar @selective; + my @offline = grep( /(\d|[Bb]ackground|[Ff]oreground)+\ +[Oo]ffline/, @outputA ); + $IDs{'offline'} = scalar @offline; + + # if we have logs, actually grab the log output + if ( $IDs{'completed'} > 0 + || $IDs{'interrupted'} > 0 + || $IDs{'read_failure'} > 0 + || $IDs{'extended'} > 0 + || $IDs{'short'} > 0 + || $IDs{'conveyance'} > 0 + || $IDs{'selective'} > 0 + || $IDs{'offline'} > 0 ) + { + my @headers = grep( /(Num\ +Test.*LBA| Description .*[Hh]ours)/, @outputA ); + + my @log_lines; + push( @log_lines, @extended, @short, @conveyance, @selective, @offline ); + $IDs{'selftest_log'} = join( "\n", @headers, sort(@log_lines) ); + } ## end if ( $IDs{'completed'} > 0 || $IDs{'interrupted'...}) + + # get the drive serial number, if needed + $disk_id = $name; + $output = `$smartctl -i $disk`; + # generally upper case, HP branded drives seem to report with lower case n + while ( $output =~ /(?i)Serial Number:(.*)/g ) { + $IDs{'serial'} = $1; + $IDs{'serial'} =~ s/^\s+|\s+$//g; + } + if ($useSN) { + $disk_id = $IDs{'serial'}; + } - while ( $output =~ /(?i)Model Number:(.*)/g ) { - $IDs{'model_number'} = $1; - $IDs{'model_number'} =~ s/^\s+|\s+$//g; - } + while ( $output =~ /(?i)Model Family:(.*)/g ) { + $IDs{'model_family'} = $1; + $IDs{'model_family'} =~ s/^\s+|\s+$//g; + } - while ( $output =~ /(?i)Firmware Version:(.*)/g ) { - $IDs{'fw_version'} = $1; - $IDs{'fw_version'} =~ s/^\s+|\s+$//g; - } + while ( $output =~ /(?i)Device Model:(.*)/g ) { + $IDs{'device_model'} = $1; + $IDs{'device_model'} =~ s/^\s+|\s+$//g; + } - # mainly HP drives using this - while ( $output =~ /(?i)Vendor:(.*)/g ) { - $IDs{'vendor'} = $1; - $IDs{'vendor'} =~ s/^\s+|\s+$//g; - } + while ( $output =~ /(?i)Model Number:(.*)/g ) { + $IDs{'model_number'} = $1; + $IDs{'model_number'} =~ s/^\s+|\s+$//g; + } - while ( $output =~ /(?i)Product:(.*)/g ) { - $IDs{'product'} = $1; - $IDs{'product'} =~ s/^\s+|\s+$//g; - } + while ( $output =~ /(?i)Firmware Version:(.*)/g ) { + $IDs{'fw_version'} = $1; + $IDs{'fw_version'} =~ s/^\s+|\s+$//g; + } - while ( $output =~ /(?i)Revision:(.*)/g ) { - $IDs{'revision'} = $1; - $IDs{'revision'} =~ s/^\s+|\s+$//g; - } + # mainly HP drives + while ( $output =~ /(?i)Vendor:(.*)/g ) { + $IDs{'vendor'} = $1; + $IDs{'vendor'} =~ s/^\s+|\s+$//g; + } - # figure out what to use for the max temp, if there is one - if ( $IDs{'190'} =~ /^\d+$/ ) { - $IDs{max_temp} = $IDs{'190'}; - } elsif ( $IDs{'194'} =~ /^\d+$/ ) { - $IDs{max_temp} = $IDs{'194'}; - } - if ( $IDs{'194'} =~ /^\d+$/ && defined( $IDs{max_temp} ) && $IDs{'194'} > $IDs{max_temp} ) { - $IDs{max_temp} = $IDs{'194'}; - } + # mainly HP drives + while ( $output =~ /(?i)Product:(.*)/g ) { + $IDs{'product'} = $1; + $IDs{'product'} =~ s/^\s+|\s+$//g; + } - $output = `$smartctl -H $disk`; - if ( $output =~ /SMART\ overall\-health\ self\-assessment\ test\ result\:\ PASSED/ ) { - $IDs{'health_pass'} = 1; - } elsif ( $output =~ /SMART\ Health\ Status\:\ OK/ ) { - $IDs{'health_pass'} = 1; - } + # mainly HP drives + while ( $output =~ /(?i)Revision:(.*)/g ) { + $IDs{'revision'} = $1; + $IDs{'revision'} =~ s/^\s+|\s+$//g; + } + + # figure out what to use for the max temp, if there is one + if ( $IDs{'190'} =~ /^\d+$/ ) { + $IDs{max_temp} = $IDs{'190'}; + } elsif ( $IDs{'194'} =~ /^\d+$/ ) { + $IDs{max_temp} = $IDs{'194'}; + } + if ( $IDs{'194'} =~ /^\d+$/ && defined( $IDs{max_temp} ) && $IDs{'194'} > $IDs{max_temp} ) { + $IDs{max_temp} = $IDs{'194'}; + } - $to_return->{data}{disks}{$disk_id} = \%IDs; + $output = `$smartctl -H $disk`; + if ( $output =~ /SMART\ overall\-health\ self\-assessment\ test\ result\:\ PASSED/ ) { + $IDs{'health_pass'} = 1; + } elsif ( $output =~ /SMART\ Health\ Status\:\ OK/ ) { + $IDs{'health_pass'} = 1; + } + + if ( !$IDs{'health_pass'} ) { + $to_return->{data}{unhealthy}++; + } + } ## end else [ if ( $IDs{exit} != 0 ) ] + # only bother to save this if useSN is not being used + if ( !$useSN ) { + $to_return->{data}{disks}{$disk_id} = \%IDs; + } } ## end foreach my $line (@disks) my $toReturn = $json->encode($to_return); From e7dd73860272ea6287afbd4f868fa27126527ab3 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Mon, 26 Jun 2023 11:18:38 -0500 Subject: [PATCH 18/20] improve cciss guess --- snmp/smart-v1 | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index 5dc985530..eb4f4ae22 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -273,14 +273,14 @@ if ( defined( $opts{g} ) ) { if ( $scan_modes->{'cciss_vol_status'} && ( $^O eq 'linux' || $^O eq 'freebsd' ) ) { my $cciss; if ( $^O eq 'freebsd' ) { - $cciss = '/dev/ciss'; + $cciss = 'ciss'; } elsif ( $^O eq 'linux' ) { - $cciss = '/dev/sg'; + $cciss = 'sg'; } # generate the initial device path that will be checked my $sg_int = 0; - my $device = $cciss . $sg_int; + my $device = '/dev/' . $cciss . $sg_int; my $sg_process = 1; if ( -e $device ) { @@ -339,11 +339,14 @@ if ( defined( $opts{g} ) ) { $drive_count++; } ## end while ($continue) } else { - my $drive_count = 0; - while ( $output =~ /(?i)\ *Physical drives:(.*)/g ) { - my $drive_count_line = $1; - $drive_count_line =~ s/^\s+|\s+$//g; - $drive_count += $drive_count_line; + my $sg_drive_int = 0; + my $drive_count = 0; + # count the connector lines, this will make sure failed are founded as well + while ( $output =~ /(connector +\d.*box +\d.*bay +\d.*)/g ) { + if ( !defined( $seen_lines->{$1} ) ) { + $seen_lines->{$1} = 1; + $drive_count++; + } } my $drive_int = 0; while ( $drive_int < $drive_count ) { @@ -355,7 +358,7 @@ if ( defined( $opts{g} ) ) { } ## end else [ if ( $? != 0 && !$opts{C} ) ] $sg_int++; - $device = $cciss . $sg_int; + $device = '/dev/' . $cciss . $sg_int; } ## end while ( -e $device && $sg_process ) } ## end if ( $scan_modes->{'cciss_vol_status'} && ...) From dc549462d0f70c1099accaae6f83744c724e6b2a Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Mon, 26 Jun 2023 12:03:56 -0500 Subject: [PATCH 19/20] cleanup the cciss checks some more --- snmp/smart-v1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index eb4f4ae22..b90d5d174 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -284,7 +284,7 @@ if ( defined( $opts{g} ) ) { my $sg_process = 1; if ( -e $device ) { - my $output = `cciss_vol_status -V $device 2> /dev/null`; + my $output = `which cciss_vol_status 2> /dev/null`; if ( $? != 0 && !$opts{C} ) { $sg_process = 0; $drive_lines @@ -300,10 +300,10 @@ if ( defined( $opts{g} ) ) { my $seen_lines = {}; while ( -e $device && $sg_process ) { my $output = `cciss_vol_status -V $device 2> /dev/null`; - if ( $? != 0 && !$opts{C} ) { + if ( $? != 0 && $output eq '' && !$opts{C} ) { # just empty here as we just want to skip it if it fails and there is no C # warning is above - } elsif ( $? != 0 && $opts{C} ) { + } elsif ( $? != 0 && $output eq '' && $opts{C} ) { my $drive_count = 0; my $continue = 1; while ($continue) { @@ -355,7 +355,7 @@ if ( defined( $opts{g} ) ) { $drive_int++; } - } ## end else [ if ( $? != 0 && !$opts{C} ) ] + } ## end else [ if ( $? != 0 && $output eq '' && !$opts{C})] $sg_int++; $device = '/dev/' . $cciss . $sg_int; From 65bd4fe9fba5485054a41c661e717415ab39ca51 Mon Sep 17 00:00:00 2001 From: "Zane C. Bowers-Hadley" Date: Mon, 26 Jun 2023 22:17:23 -0500 Subject: [PATCH 20/20] convert to IO::Compress::Gzip and update docs --- snmp/smart-v1 | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/snmp/smart-v1 b/snmp/smart-v1 index b90d5d174..d3b9bbdd6 100755 --- a/snmp/smart-v1 +++ b/snmp/smart-v1 @@ -30,11 +30,11 @@ Add this to snmpd.conf like below. Then add to root's cron tab, if you have more than a few disks. - */3 * * * * /etc/snmp/smart -u + */5 * * * * /etc/snmp/extends/smart -u You will also need to create the config file, which defaults to the same path as the script, but with .config appended. So if the script is located at /etc/snmp/smart, the config file -will be /etc/snmp/smart.config. Alternatively you can also specific a config via -c. +will be /etc/snmp/extends/smart.config. Alternatively you can also specific a config via -c. Anything starting with a # is comment. The format for variables is $variable=$value. Empty lines are ignored. Spaces and tabes at either the start or end of a line are ignored. Any @@ -65,6 +65,31 @@ used for reporting and everything after that is used as the argument to be passe If you want to guess at the configuration, call it with -g and it will print out what it thinks it should be. + +Switches: + +-c The config file to use. +-u Update +-p Pretty print the JSON. +-Z GZip+Base64 compress the results. + +-g Guess at the config and print it to STDOUT +-C Enable manual checking for guess and cciss. +-S Set useSN to 0 when using -g +-G Guess modes to use. This is a comma seperated list. + Default :: scan-open,cciss-vol-status + +Guess Modes: + +- scan :: Use "--scan" with smartctl. "scan-open" will take presidence. + +- scan-open :: Call smartctl with "--scan-open". + +- cciss-vol-status :: Freebsd/Linux specific and if it sees /dev/sg0(on Linux) or + /dev/ciss0(on FreebSD) it will attempt to find drives via cciss-vol-status, + and then optionally checking for disks via smrtctl if -C is given. Should be noted + though that -C will not find drives that are currently missing/failed. + =cut ## @@ -75,7 +100,7 @@ use strict; use Getopt::Std; use JSON; use MIME::Base64; -use Gzip::Faster; +use IO::Compress::Gzip qw(gzip $GzipError); my $cache = '/var/cache/smart'; my $smartctl = '/usr/bin/env smartctl'; @@ -107,7 +132,8 @@ Scan Modes: - cciss-vol-status :: Freebsd/Linux specific and if it sees /dev/sg0(on Linux) or /dev/ciss0(on FreebSD) it will attempt to find drives via cciss-vol-status, - falling if that is not found to checking for disks via smrtctl. + and then optionally checking for disks via smrtctl if -C is given. Should be noted + though that -C will not find drives that are currently missing/failed. '; } ## end sub main::HELP_MESSAGE @@ -782,13 +808,15 @@ if ( !$opts{p} ) { } if ( $opts{Z} ) { - my $compressed = encode_base64( gzip($toReturn) ); + my $toReturnCompressed; + gzip \$toReturn => \$toReturnCompressed; + my $compressed = encode_base64($toReturnCompressed); $compressed =~ s/\n//g; $compressed = $compressed . "\n"; if ( length($compressed) < length($toReturn) ) { $toReturn = $compressed; } -} +} ## end if ( $opts{Z} ) if ( !$noWrite ) { open( my $writefh, ">", $cache ) or die "Can't open '" . $cache . "'";