#!/usr/bin/env perl # Copyright (c) 2026, rodolico # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # runSmart - Sequential SMART test execution on all system drives # # PURPOSE: # Discovers all drives in the system and executes short SMART tests sequentially # to diagnose potential hardware issues. Processes drives one at a time to minimize # system performance impact during testing. # # USAGE: # Standalone: ./runSmart # From sneakernet: Executed automatically during cleanup phase on target server # From Perl script: my $results = eval { do './runSmart' }; # # EXECUTION CONTEXT: # When executed by sneakernet on the target server: # - Script is read from transport drive, decrypted, and executed via eval() # - Progress messages sent to STDERR for real-time monitoring # - Summary results returned via return statement for logging # - When run standalone, prints results to STDOUT # # BEHAVIOR: # 1. Discovers all drives using smartctl --scan # 2. Starts short SMART test on each drive sequentially # 3. Monitors test progress with periodic status checks # 4. Collects and reports results for each drive # 5. Returns summary of all test outcomes # # REQUIREMENTS: # - smartmontools package (provides smartctl command) # - Root/sudo privileges for drive access # - Drive must support SMART functionality # # OUTPUT: # Real-time: Progress messages to STDERR during execution # Summary: Test results for each drive (return value or STDOUT) # # Author: rodolico # Version: 1.0 # Updated: 2026-01-16 use strict; use warnings; use 5.010; # Get list of all drives my @drives = `smartctl --scan | awk '{print \$1}' | sed 's|/dev/||' | sort`; chomp @drives; my @result; my @errorMessages = (); unless (@drives) { push @errorMessages, "No drives found"; if (caller()) { return ( "", join("\n", @errorMessages) ); } else { print "\n=== CLEANUP SCRIPT ERRORS ===\n"; print join("\n", @errorMessages) . "\n"; exit 1; } } print STDERR "Found drives: " . join(", ", @drives) . "\n"; print STDERR "Starting sequential SMART tests...\n"; push @result, "Drives found: " . join(", ", @drives); push @result, "SMART Test Results:"; foreach my $drive (@drives) { print STDERR "=" x 60 . "\n"; print STDERR "Testing /dev/$drive\n"; print STDERR "=" x 60 . "\n"; # Start the test system("smartctl -t short /dev/$drive"); # Wait for test to complete print STDERR "Waiting for test to complete...\n"; my $completed = 0; while (!$completed) { sleep 30; my $status = `smartctl -a /dev/$drive`; my $linenumber = 0; # Check if test is still running if ($status =~ /Self-test execution status:\s+\(\s*\d+\)\s+Self-test routine in progress/i) { print STDERR "."; } elsif ($status =~ /Self-test execution status:\s+\(\s*0\)/i) { $completed = 1; push @result, "drive $drive\tstatus => PASSED"; print STDERR "\nTest completed!\n"; } else { push @result, "drive $drive\tstatus => UNKNOWN/FAILED"; push @errorMessages, "drive $drive\tstatus => UNKNOWN/FAILED"; $completed = 1; print STDERR "\nTest status unknown or completed with error\n"; } } # Get and display results print STDERR "\nTest Results for /dev/$drive:\n"; print STDERR "-" x 60 . "\n"; system("smartctl -l selftest /dev/$drive | head -20"); print STDERR "-" x 60 . "\n"; } print STDERR "=" x 60 . "\n"; print STDERR "All tests completed!\n"; print STDERR "=" x 60 . "\n"; # When run standalone, print to STDOUT if (caller()) { return ( join( "\n", @result ) . "\n", @errorMessages ? join("\n", @errorMessages) : "" ); } else { print "Results Summary:\n"; print join( "\n", @result ) . "\n"; if (@errorMessages) { print "\n=== CLEANUP SCRIPT ERRORS ===\n"; print join("\n", @errorMessages) . "\n"; } } # End of script