#! /usr/bin/env perl # finds missing drives, telling you what bay they are in. # Author: R. W. Rodolico # Date: 2025-04-04 # # run smartctl --scan to determine what drives are in the system, then # again for each drive to get the serial number off of the drives. # These end up in @active # # If there is stuff in STDIN, will assume that is the input and # search it for lines which do not match the list of serial numbers # from @active. # # If STDIN is not active, looks for either of the files 'drive_bays', # then 'drives.tsv' in both /etc and /usr/local/etc. If one of those # is found, will read it in, searching lines which do not match the list # of serial numbers in @active # # Any matching lines (those not found in @active) are printed to the # screen # use strict; use warnings; use IO::Select; # this appears to be available in base Perl installs sub loadFile { my ( $filename, $regex, @paths ) = @_; my @return; while ( my $path = shift @paths ) { if ( -f "$path\/$filename" ) { open DATA, "<$path\/$filename" or die "Could not open $path\/$filename: $!\n"; @return = grep{ ! /$regex/ } ; chomp @return; return @return; } } return undef; } # get list of active drives from smartctl my @drives = qx( smartctl --scan | cut -d' ' -f1 ); chomp @drives; my @active; # for each drive found, get its serial number. Note: using geom might be faster foreach my $drive ( @drives ) { push @active, qx( smartctl -a $drive | grep 'Serial Number:' | rev | cut -d' ' -f1 | rev ); } chomp @active; # build a regex that contains all of the serial numbers found my $regex = '(' . join( '|', @active ) . ')'; my @lines; # a place to put our input # turn off STDIN for blocking read my $selector = IO::Select->new(); $selector->add(\*STDIN); if ( $selector->can_read(0) ) { # This is true only if there is something on STDIN, ie a pipe @lines = grep{ ! /$regex/ } <>; } # if STDIN didn't work, look for file drive_bays @lines = &loadFile( 'drive_bays', $regex, '/etc', '/usr/local/etc' ) unless @lines; # if we still didn't do it, try the file drives.tsv @lines = &loadFile( 'drives.tsv', $regex, '/etc', '/usr/local/etc' ) unless @lines; # OMG, nothing worked. Tell the user push @lines, "Could not find STDIN, drive_bays or drives.tsv, no values" unless @lines; chomp @lines; print join("\n", @lines) . "\n"; 1;